P10546 题解
DaiRuiChen007 · · 题解
Problem Link
题目大意
交互器有
n 个点n-1 条边的有向图,将图视为无向图后是一棵树。你可以进行如下操作,每次可以翻转若干条边,你会知道这种情况下每个点
u 能被多少个点到达,记为f_u 。请在
50 次操作内还原整张图,即确定每条边的起点和终点。数据范围:
n\le 10^4 。
思路分析
记询问次数
对于树上问题,考虑从叶子出发逐步剥离每个点。
我们要先求出叶子,注意到如果某个叶子
假如每条边都随机,那么有
考虑一个经典的技巧,我们对每条边,随机选择
那么对于一个叶子结点
对于一个非叶子节点,他的两条出边至少有一个时刻不同向,那么这个点满足
那么这样就能每次求出一个叶子结点
确定
然后考虑找父亲,注意到
考虑什么时候会将一个错误的
那么我们知道所有情况中
因此判断错误的概率为
时间复杂度
代码呈现
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int Q=50,MAXN=10005;
const ll A=(1ll<<Q)-1;
mt19937 rnd(time(0));
ll gen() {
vector <int> p;
for(int i=0;i<Q;++i) p.push_back(i);
shuffle(p.begin(),p.end(),rnd);
ll z=0;
for(int i=0;i<Q/2;++i) z|=1ll<<p[i];
return z;
}
int n,f[Q+5][MAXN],w[Q+5][MAXN],c[MAXN],U[MAXN],V[MAXN];
ll S[MAXN];
bool del[MAXN];
unordered_map <ll,int> E;
signed main() {
cin>>n;
for(int i=1;i<n;++i) {
ll x=gen();
while(E.count(x)) x=gen();
S[i]=x,E[x]=i,E[A^x]=-i;
}
for(int i=0;i<Q;++i) {
cout<<"? ";
for(int u=1;u<n;++u) cout<<(S[u]>>i&1);
cout<<endl;
for(int u=1;u<=n;++u) cin>>f[i][u],w[i][u]=1,c[u]+=(f[i][u]==1);
}
for(int o=1;o<n;++o) {
int x=0,y=0;
for(int u=1;u<=n;++u) if(!del[u]&&c[u]==Q/2) { x=u; break; }
ll I=0; del[x]=true;
for(int i=0;i<Q;++i) if(f[i][x]>w[i][x]) I|=1ll<<i;
for(int u=1;u<=n;++u) if(!del[u]) {
bool flg=1;
for(int i=0;i<Q;++i) if((I>>i&1)&&f[i][x]-w[i][x]!=f[i][u]) {
flg=0; break;
}
if(!flg) continue;
y=u; break;
}
for(int i=0;i<Q;++i) if(!(I>>i&1)) w[i][y]+=w[i][x],c[y]+=(w[i][y]==f[i][y]);
int z=E[I];
if(z>0) U[z]=x,V[z]=y;
else U[-z]=y,V[-z]=x;
}
cout<<"! ";
for(int i=1;i<n;++i) cout<<U[i]<<" "<<V[i]<<" ";
cout<<endl;
return 0;
}