BPG_ning
2024-11-16 21:23:30
牛逼构造,硬控我 2h。
一些注释:
1.身份高/低:身份高指好人面大,身份低指好人面小,可以理解为好人权值为
2.同底色:身份相同,同为好人或坏人。
找到一个好人然后
考虑当
考虑这么做为什么是对的。经常玩狼人杀的小朋友们应该知道一个结论:若
证明如下:若
那么我们刚刚所找的一个强连通分量也就意味着一些同底色的人,由于好人策略固定,所以好人一定在一个强连通分量,而坏人不能在。怎么找到这个强连通分量呢?就是那个最大的强连通分量!因为大小等于
这个
经常玩狼人杀的小朋友们应该又知道一个结论:若
在狼人杀中这是一般情况,但是在这道题中是一定正确的,因为两个好人不会互相判定为坏人。
一个很妙的 Trick 就是每次抓到一对
精彩的部分来了,我们将上述两个定理结合,维护一个内向森林,一条边
每次找两个树根 全员恶人),那么每次同时删两棵树的叶子直到一棵树空,这样每次删的一对至少有一个是坏人,剩下的人就一定是好人。
如上两种操作都至少让树的数量减少一,那么至多做
代码:
#include <bits/stdc++.h>
using namespace std;
const int N=4e3+10;
int n,a,b,rt,vis[N];
vector<int> G[N];
struct FaC{
int fa[N],sz[N];
void init(){for(int i=1;i<=n;i++) fa[i]=i,sz[i]=1;}
int find(int x){return (fa[x]==x?x:fa[x]=find(fa[x]));}
void merge(int x,int y){
x=find(x),y=find(y);
fa[x]=y,sz[y]+=sz[x];
}
int size(int u){return sz[u];}
}F;
int ask(int x,int y){
cout<<"? "<<x-1<<' '<<y-1<<endl;
char s;
cin>>s;
return (s=='Y');
}
void del(int x){
for(int y:G[x]) if(!vis[y]){
del(y);
return ;
}
vis[x]=1;
return ;
}
int main(){
// ios::sync_with_stdio(false);
// std::cin.tie(0);
// std::cout.tie(0);
// freopen("nzq.in","r",stdin);
// freopen("nzq.out","w",stdout);
cin>>a>>b;
if(a<=b){
cout<<"Impossible"<<endl;
return 0;
}
n=a+b;
F.init();
for(int c=1;c<n;c++){
int u=-1,v=-1;
for(int i=1;i<=n;i++) if(!vis[i] && F.find(i)==i){
if(u==-1) u=i;
else v=i;
}
if(v==-1) break;
int op=ask(u,v);
if(op==1){
F.merge(u,v);
G[v].push_back(u);
}else{
for(int j=1;j<=min(F.size(u),F.size(v));j++){
del(u),del(v);
}
}
}
for(int i=1;i<=n;i++) if(!vis[i]) rt=i;
string s="";
for(int i=1;i<=n;i++){
if(rt==i) s=s+'1';
else s=s+(char)(ask(rt,i)+'0');
}
cout<<"! "<<s<<endl;
cerr<<1.0*clock()/CLOCKS_PER_SEC<<'\n';
return 0;
}