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;
}