Codeforces Round #648 (Div. 2) 简要题解

A

每一次操作会消去一个没被占用的行和一个没被占用的列,取个min之后判奇偶性即可。

int n,m;
bool vis1[110],vis2[110];

int main()
{
    int T=read();
    while (T--)
    {
        n=read();m=read();
        rep(i,1,n) vis1[i]=0;
        rep(i,1,m) vis2[i]=0;
        int c1=n,c2=m;
        rep(i,1,n)
        {
            rep(j,1,m)
            {
                int x=read();
                if (!x) continue;
                if (!vis1[i]) {vis1[i]=1;c1--;}
                if (!vis2[j]) {vis2[j]=1;c2--;}
            }
        }
        int tmp=min(c1,c2);
        if (tmp&1) puts("Ashish");else puts("Vivek");
    }
    return 0;
}

B

注意到如果\(0\)\(1\)同时存在的话,我们可以借助一个不同类型的数来完成两个相同类型的数的交换,所以此时一定可以将序列排序。

只存在一种类型的数时,无法进行任何操作,判断当前序列是否已排好序即可。

int n,a[1010],b[1010],c[1010];

int main()
{
    int T=read();
    while (T--)
    {
        n=read();
        rep(i,1,n) a[i]=read();
        rep(i,1,n) b[i]=read();
        int c0,c1;c0=c1=0;
        rep(i,1,n)
            if (b[i]) c1=1;else c0=1;
        if ((c0) && (c1)) puts("Yes");
        else
        {
            rep(i,1,n) c[i]=a[i];
            sort(c+1,c+1+n);
            int ok=1;
            rep(i,1,n)
                if (a[i]!=c[i]) ok=0;
            if (ok) puts("Yes");else puts("No");
        }
    }
    return 0;
}

C

首先只用考虑向右位移,考虑若第\(i\)位上的两个数相同时需要的位移大小,这个大小可以被唯一确定,开个桶记一下即可。

int n,a[200200],b[200200],c[200200],p[200200];

int main()
{
    n=read();
    rep(i,1,n) a[i]=read();
    rep(i,1,n) {b[i]=read();p[b[i]]=i;}
    rep(i,1,n)
    {
        int x=a[i],pos=p[x];
        int cnt=(i+n-pos);
        if (cnt>=n) cnt-=n;
        c[cnt]++;
    }
    int ans=0;
    rep(i,0,n-1) ans=max(ans,c[i]);
    printf("%d",ans);
    return 0;
}

D

最直接的想法是不让B能四处移动,于是把每个B围一圈即可。

注意特判掉G与B相连的情况。

int n,m,sq[60][60],vis[60][60];
char s[60][60];
const int dx[]={1,0,-1,0},dy[]={0,1,0,-1};
queue<pii> q;

bool valid(int x,int y)
{
    return ((x>=1) && (x<=n) && (y>=1) && (y<=m));
}

void bfs(int stx,int sty)
{
    if (!sq[stx][sty]) return;
    q.push(mkp(stx,sty));vis[stx][sty]=1;
    while (!q.empty())
    {
        pii now=q.front();q.pop();
        int x=now.fir,y=now.sec;
        rep(i,0,3)
        {
            int nx=x+dx[i],ny=y+dy[i];
            if ((valid(nx,ny)) && (sq[nx][ny]) && (!vis[nx][ny]))
            {
                vis[nx][ny]=1;
                q.push(mkp(nx,ny));
            }
        }
    }
}

int main()
{
    int T=read();
    while (T--)
    {
        n=read();m=read();
        rep(i,1,n) 
        {
            scanf("%s",s[i]+1);
            rep(j,1,m) sq[i][j]=(s[i][j]!='#');
        }
        int ok=1;
        rep(i,1,n) 
        {
            rep(j,1,m)
            {
                if (s[i][j]=='B')
                {
                    rep(k,0,3)
                    {
                        int nx=i+dx[k],ny=j+dy[k];
                        if (valid(nx,ny))
                        {
                            if (s[nx][ny]=='G') {ok=0;break;}
                            sq[nx][ny]=0;
                        }
                    }
                    if (!ok) break;
                }
            }
            if (!ok) break;
        }
        if (!ok) {puts("No");continue;}
        bfs(n,m);
        rep(i,1,n) rep(j,1,m)
        {
            if ((s[i][j]=='G') && (!vis[i][j])) {ok=0;break;}
        }
        if (!ok) puts("No");else puts("Yes");
        rep(i,1,n) rep(j,1,m) vis[i][j]=0;
    }
    return 0;
}

E

降智*1

考虑最后集合可能的大小,注意到若当前集合为\(3\),新增加的元素不可能会在新的二进制位上产生贡献,因为此时已经有\(3\)个数不具有这个二进制位了,而这个不会贡献新的二进制位的元素显然是可以删掉的。

所以我们只需要枚举不超过\(3\)的集合,取个or后再取个max即可。

int n;
ll a[510],ans=0;

int main()
{
    n=read();
    rep(i,1,n) a[i]=readll();
    ans=0;
    rep(i,1,n) rep(j,i,n) rep(k,j,n)
        ans=max(ans,a[i]|a[j]|a[k]);
    printf("%lld",ans);
    return 0;
}

F

降智*2

一个很重要的观察是:无论如何对数列\(a\)进行操作,数对\((a_i,a_{n-i+1})\)是不会被拆散的,于是我们得到了一个合法的必要条件。接下来考虑根据\(b\)序列从中间向两边构造,不难发现这样的构造方案是一定存在的。所以这个条件也是一个充分条件。开一个map维护所有的数对即可。

int n,a[510],b[510];
map<pii,int> mp;

int main()
{
    int T=read();
    while (T--)
    {
        n=read();mp.clear();
        rep(i,1,n) a[i]=read();
        rep(i,1,n) b[i]=read();
        rep(i,1,n/2) mp[mkp(a[i],a[n-i+1])]++;
        int ok=1;
        rep(i,1,n/2)
        {
            int x=b[i],y=b[n-i+1];
            if (mp[mkp(x,y)])
            {
                mp[mkp(x,y)]--;
            }
            else if (mp[mkp(y,x)]) 
            {
                mp[mkp(y,x)]--;
            }
            else {ok=0;break;}
        }
        if ((n&1) && (a[(n+1)/2]!=b[(n+1)/2])) ok=0;
        if (ok) puts("Yes");else puts("No");
    }
    return 0;
}

G

一道有点意思(?)的交互.

一个比较直接的想法是:将所有的下标的二进制表示写出来,记\(x_i\)为从低到高第\(i\)个二进制位为1的所有数的or值,那么在求\(P_i\)的时候只需要将i中为\(0\)的二进制位对应的\(a\)取出来取or。

但是这样会有一个问题,如果一个数\(j\)\(i\)所有的二进制位,那么我们算出来的\(P_i\)是不会包含\(a_j\)的。最直接的处理方法就是再询问出\(y_i\)表示从低到高第\(i\)个二进制位为0的\(a_i\)的or值。但是这样的话询问次数就爆炸了。

考虑如何避免上面出现的问题,我们需要给原来的下标赋上一个新的下标,使得不存在两个数\(x,y\)满足\(x\)不会拥有\(y\)所有的\(1\)二进制位。更形式化的,我们需要求出\(n\)个集合满足任意两个集合不存在包含关系。

根据这个定理发现对于一个大小为\(13\)的集合,它的所有\(6\)元子集正好\(>1000\)且满足互不包含,这也是满足这个条件的一个下界。

int n,sta[1010];
ll ans[1010],cnt[10010],val[20];
vi q[20];

ll ask(vi id)
{
    int len=id.size();
    if (!len) return 0;
    printf("? %d ",len);
    rep(i,0,len-1) printf("%d ",id[i]);
    puts("");fflush(stdout);
    ll val=readll();
    return val;
}

void answer()
{
    printf("! ");
    rep(i,1,n) printf("%lld ",ans[i]);
    puts("");fflush(stdout);
}

int main()
{
    n=read();int lim=(1<<13)-1;
    rep(i,1,lim) cnt[i]=cnt[i>>1]+(i&1);
    int now=0;
    rep(i,1,lim)
    {
        if (cnt[i]!=6) continue;
        sta[++now]=i;
        rep(j,0,12)
            if ((i>>j)&1) q[j].pb(now);
        if (now==n) break;
    }
    rep(i,0,12) val[i]=ask(q[i]);
    rep(i,1,n)
    {
        rep(j,0,12)
            if (((sta[i]>>j)&1)==0) 
            {
                ans[i]|=val[j];
            }
    }
    answer();
    return 0;
}
posted @ 2020-06-08 21:46  EncodeTalker  阅读(138)  评论(0编辑  收藏