atcoder Keyence Programming Contest 2020

A Painting

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,m,k;
int main()
{
    
    
    cin>>n>>m>>k;
    if(n<m) swap(n,m);
    int ans=0;
    while(k>0)
    {
    
    
        k-=n;
        ans++;
    }
    cout<<ans;
}

B Robot Arms
记录左右端点,按端点排序后就是算法书上的经典贪心问题。设 n o w now now为当前所占区间的最右的端点,初始 n o w = 0 now=0 now=0,将左端点按从小到大排序,然后按顺序遍历,如果下一步满足 l i > n o w l_i>now li>now,那么 a n s + + ans++ ans++,否则令 n o w = m i n ( n o w , r i ) now=min(now,r_i) now=min(now,ri)

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
struct node
{
    
    
    int l,r;
    bool operator<(const node&o)const
    {
    
    
        return l<o.l;
    }
}a[N];
int n;
int main()
{
    
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
    
    
        int x,p;
        scanf("%d%d",&x,&p);
        a[i].l=x-p;
        a[i].r=x+p;
    }
    sort(a+1,a+1+n);
    int ans=0,now=-1e9;
    now-=10;
    for(int i=1;i<=n;i++)
        if(a[i].l>=now)
    {
    
    
        ans++;now=a[i].r;
    }
    else now=min(now,a[i].r);
    printf("%d\n",ans);
}

C Subarray Sum
比200分的B题还简单,不是吗。

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,k,s,a[N];
int main()
{
    
    
    scanf("%d%d%d",&n,&k,&s);
    if(s<1000000000)
    {
    
    
        for(int i=1;i<=k;i++) a[i]=s;
        for(int i=k+1;i<=n;i++) a[i]=s+1;
    }
    else
    {
    
    
        for(int i=1;i<=k;i++) a[i]=s;
        for(int i=k+1;i<=n;i++) a[i]=1;
    }
    for(int i=1;i<=n;i++) printf(i==n?"%d\n":"%d ",a[i]);
}

D Swap and Flip
状态压缩考虑将哪些元素排序,一开始在上面的排序后需要交换偶数次,一开始在下面的排序后需要交换奇数次。

然后考虑贪心的交换排序,每次选择离目标位置最近并且奇偶性可行的元素排序即可,并记录次数。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int N=20;
int n,a[N],b[N];
struct node
{
    
    
    int x,p;
    node(int x=0,int p=0):x(x),p(p){
    
    }
}c[N];
int d[N];
int solve(int x)
{
    
    
    for(int i=0;i<n;i++)
        if(x>>i&1)
            c[i+1]=node(b[i+1],1),d[i+1]=b[i+1];
        else
            c[i+1]=node(a[i+1],0),d[i+1]=a[i+1];
    sort(d+1,d+1+n);
    int ans=0;
    for(int i=n;i>=1;i--)
    {
    
    
        bool flag=false;
        for(int j=i;j>=1;j--)
            if(c[j].x==d[i]&&c[j].p==(i-j&1))
        {
    
    
            flag=true;
            ans+=i-j;
            for(int k=j;k<i;k++)
            {
    
    
                c[k+1].p^=1;
                swap(c[k],c[k+1]);
            }
            break;
        }
        if(!flag) return inf;
    }
    return ans;
}
int main()
{
    
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    int ans=inf;
    for(int i=0;i<1<<n;i++)
        ans=min(ans,solve(i));
    if(ans==inf) ans=-1;
    printf("%d\n",ans);
}

E Bichromization
显然,只有 a u a_u au a v a_v av相等的边才能作为生成树的切入点,随后生成一颗树即可。

相邻的点如果能保证权值不减,就放不同的颜色,边放相应的权值。

没有用到的边权值为 i n f inf inf,即可保证最短路长度不增加。

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5;
int n,m,tot,c[N],a[N],head[N],nex[N],to[N],id[N];
void add(int u,int v,int i){
    
    to[++tot]=v;nex[tot]=head[u];head[u]=tot;id[tot]=i;}
bool vis[N];
int dis[N];
int ans;
struct node
{
    
    
    int u,x;
    node(int u=0,int x=0):u(u),x(x){
    
    }
    bool operator<(const node&o)const
    {
    
    
        return x<o.x;
    }
};
priority_queue<node>q;
int main()
{
    
    
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    bool flag=false;
    for(int i=1;i<=m;i++)
    {
    
    
        int u,v;scanf("%d%d",&u,&v);
        add(u,v,i);add(v,u,i);
        if(a[u]==a[v]&&!vis[u]&&!vis[v])
        {
    
    
            q.push(node(u,a[u]));
            q.push(node(v,a[v]));
            c[u]=1;c[v]=0;
            vis[u]=vis[v]=true;
            ans+=2;
            dis[i]=a[u];
            flag=true;
        }
    }
    if(!flag){
    
    printf("-1\n");return 0;}
    while(!q.empty())
    {
    
    
        node now=q.top();q.pop();
        for(int i=head[now.u];i;i=nex[i])
        {
    
    
            int v=to[i];if(vis[v]||a[v]<now.x) continue;
            vis[v]=true;ans++;
            dis[id[i]]=a[v];
            c[v]=c[now.u]^1;
            q.push(node(v,a[v]));
        }
    }
    if(ans!=n){
    
    printf("-1\n");return 0;}
    for(int i=1;i<=n;i++)
        putchar(c[i]?'B':'W');
    putchar('\n');
    for(int i=1;i<=m;i++)
        printf("%d\n",!dis[i]?1000000000:dis[i]);
}

F - Monochromization
定义两种操作:
1.选择若干行,给每个选中的行涂上一种颜色
2.选择若干列,给每个选中的列涂上一种颜色
这样操作与原问题是等价的,问题是以上的操作通过不同的顺序会得到相同的颜色矩阵。
为了避免重复计数,定义可移除行为该行每一列颜色都相同的行,定义可移除列为该列每一行颜色都相同的列。
对于一个已经染好色的矩阵,我们考虑制定一个特定的顺序去移除这个矩阵:
A.移除所有可移除列(我们认为这是对列进行的最后一次操作,那么由于此次操作之前这些列染的颜色已经不重要了,我们可以将这些列全部从矩阵中删除)
B.移除所有可移除行(认为这是倒数第二次操作)
C.移除所有可移除列(倒数第三次操作)

直到不可移除为止,显然任意一个通过操作1,2染色出来的矩阵一定可以通过A,B,C…这样的移除操作直到矩阵不可再移除为止。那么可以发现,一个染色的矩阵通过上述移除操作只可能得出一个移除序列,可以这么说:一个颜色矩阵对应一种移除序列,但一种移除序列可以对应多个不同的颜色矩阵(这取决于移除时选择的行数、列数和颜色)。

当然我们不需要关注具体的移除序列是什么样子,上述操作每次一定是移除了一些行或一些列,那么我们要关注的其实只是移除的行数和列数。

c o l i , j , l col_{i,j,l} coli,j,l“上一步移除的是列,移除后剩余 i {i} i行和 j {j} j列未移除,上一步移除的列共有 l {l} l种不同的颜色”的方案数
r o w i , j , l row_{i,j,l} rowi,j,l“上一步移除的是行,移除后剩余 i {i} i行和 j {j} j列未移除,上一步移除的行共有 l {l} l种不同的颜色“的方案数
在纸上画画图,我们可以发现 l {l} l的转移是不增的。比如,如果当前移除行只移除了黑色的行,说明在上一步没有黑色的列可以移除,那么下一步移除列只可能移除白色的列,否则就与上一步条件冲突。
于是我们可以得出转移:
r o w k , j , 1 < − ( c o l i , j , 2 ∗ 2 + c o l i , j , 1 ) ∗ C ( i , i − k ) row_{k,j,1}<-(col_{i,j,2}*2+col_{i,j,1})*C(i,i-k) rowk,j,1<(coli,j,22+coli,j,1)C(i,ik)
i i i个行中选出 i − k i-k ik个染成某种颜色,注意从 c o l i , j , 2 {col_{i,j,2}} coli,j,2转移过来有两种颜色可选
r o w k , j , 2 < − c o l i , j , 2 ∗ C ( i , i − k ) ∗ ( p 2 ( i − k ) − 2 ) row_{k,j,2}<-col_{i,j,2}*C(i,i-k)*(p2(i-k)-2) rowk,j,2<coli,j,2C(i,ik)(p2(ik)2)
i i i个行中选出 i − k i-k ik个染两种颜色, p 2 ( x ) p2(x) p2(x)表示 2 {2} 2 x {x} x次方,表示没行都有两种颜色可选,减 2 {2} 2是减去两种颜色全相同的方案,然后列的转移同理( r o w row row c o l col col转移过来, c o l col col r o w row row转移过来,因为要保证上次操作是与这次不同的操作)。
得出上述 d p dp dp值后,枚举哪些行哪些列是没被移除的,判断这些行是否还可以被移除,如果不可以,就加上答案,否则不加上(为了避免重复计数)。
那么这个问题就可以解决 b y O ( 2 n + m ) {byO(2^{n+m})} byO(2n+m)的时间。
注意到把行直接移除成剩余 0 0 0行或者列直接移除成剩余 0 0 0列的情况是比较特殊的,我们要注意这种情况单独计数。
实现细节见代码(为了便于大家理解我加了几个注释)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=22,mod=998244353;
ll fc[N],ifc[N],p[N],col[N][N][3],row[N][N][3];
ll inv(ll x){
    
    return x<=1?x:(mod-mod/x)*inv(mod%x)%mod;}
ll C(int n,int m)
{
    
    
    return fc[n]*ifc[n-m]%mod*ifc[m]%mod;
}
ll iC(int n,int m)
{
    
    
    return inv(C(n,m));
}
int n,m,r[N],c[N];
char s[N][N];
void init()
{
    
    
    fc[0]=fc[1]=ifc[0]=ifc[1]=p[0]=1;
    for(int i=2;i<N;i++) fc[i]=fc[i-1]*i%mod,ifc[i]=(mod-mod/i)*ifc[mod%i]%mod;
    for(int i=2;i<N;i++) ifc[i]=ifc[i]*ifc[i-1]%mod;
    for(int i=1;i<N;i++) p[i]=p[i-1]*2%mod;
}
void Dp()
{
    
    
    for(int j=1;j<=m;j++)
        col[n][j][2]=C(m,j)*p[m-j]%mod;//给列赋初值
    ll ans=p[m];//直接把m列直接染完的情况
    for(int i=n;i>=1;i--)
        for(int j=m;j>=1;j--)
    {
    
    
        col[i][j][0]=(col[i][j][1]+col[i][j][2])%mod;
        for(int k=1;k<i;k++)
        {
    
    
            int d=i-k;
            (row[k][j][1]+=C(i,d)*(col[i][j][2]*2+col[i][j][1])%mod)%=mod;
            (row[k][j][2]+=C(i,d)*(p[d]-2)%mod*col[i][j][2]%mod)%=mod;
        }
        ans=(ans+col[i][j][2]*(p[i]-2))%mod;
        //当前步是列,下一步直接把行全部移除,注意不能作为相同颜色移除,某则上一步可以当列来操作
        row[i][j][0]=(row[i][j][1]+row[i][j][2])%mod;
        for(int k=1;k<j;k++)
        {
    
    
            int d=j-k;
            (col[i][k][1]+=C(j,d)*(row[i][j][2]*2+row[i][j][1])%mod)%=mod;
            (col[i][k][2]+=C(j,d)*(p[d]-2)%mod*row[i][j][2]%mod)%=mod;
        }
        ans=(ans+row[i][j][2]*(p[j]-2))%mod;
        //当前步是行,下一步直接把列全部移除,注意不能作为相同颜色移除,某则上一步可以当行来操作
    }
    int upn=1<<n,upm=1<<m;
    for(int i=1;i<upn;i++)
        for(int j=1;j<upm;j++)
    {
    
    
        bool flag=true;
        for(int k=0;k<n;k++)
            if(i>>k&1)
        {
    
    
            int w=r[k+1]&j;
            if(w==j||w==0) flag=false;//判断此行剩余元素是否全相等
        }
        for(int k=0;k<m;k++)
            if(j>>k&1)
        {
    
    
            int w=c[k+1]&i;
            if(w==i||w==0) flag=false;//判断此列剩余元素是否全相等
        }
        if(!flag) continue;//如果存在相等说明还能够移除,不计算这里的答案
        int x=__builtin_popcount(i),y=__builtin_popcount(j);
        ans=(ans+(row[x][y][0]+col[x][y][0])*iC(n,x)%mod*iC(m,y))%mod;
        //因为这里特定的行列已经选出来了,所以除去组合数
    }
    printf("%lld\n",ans);
}
int main()
{
    
    
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        if(s[i][j]=='.') r[i]|=1<<j>>1,c[j]|=1<<i>>1;
    Dp();
}

猜你喜欢

转载自blog.csdn.net/Huah_2018/article/details/104048900