CF1158E
题意:有一颗 $n \leq 10^3$ 个点的有标号无根树,你需要通过不超过 $80$ 次询问确定树的形态。
每次询问可以对每一个点指定一个权值 $v_i$ ,并把所有满足 $d(i,j) \in [1,v_i]$ 的点 $j$ 染色, $d(i,j)$ 表示树上点 $i$ 到点 $j$ 路径边数。
每次交互库返回每个点是否被染色,询问之间互不影响(就是这一次询问被染色的点不会保留到下一次询问)。
首先以点 $1$ 为根,求出每一个点的深度。
如果知道所有深度为 $d$ 的点,然后令它们的权值均为 $k$ ,则可以得到所有深度在 $[d-k,d+k]$ 中的点。
由于深度为 $0$ 的点只有 $1$ 号点,所以可以用两次询问得知确定深度的所有点。
假设已知所有点深度除 $2^k$ 下取整的值,即 $\lfloor\frac{dep_i}{2^k} \rfloor$ 。
以及所有深度是 $2^k$ 倍数的点,则再用 $4$ 次询问可以确定:
$(1).$ 所有的 $\lfloor\frac{dep_i}{2^{k-1}} \rfloor$ 。
$(2).$ 所有深度是 $2^{k-1}$ 倍数的点。
就是将深度是 $2^k$ 的倍数的点分成两类,一类深度是 $2^{k+1}$ 的倍数,一类不是。
将一类点权值全部设为 $2^{k-1}-1$ ,然后没有标记的点深度范围已知,结合之前信息可以得出 $(1)$ 。
分两类是为了让不同点互不干扰。
再将点权设为 $2^{k-1}$ ,结合之前未被标记的点可得 $(2)$ 。
不理解自己画个图看看就好了,实在不行看代码吧。
已知每个点的深度之后,考虑求出每个点父亲的编号。
先考虑解决最高深度为 $3$ 的子问题:按位考虑。
考虑把所有编号二进制第 $i$ 位为 $1$ 且深度为 $2$ 的点权值设为 $1$ ,则可以确定每个深度为 $3$ 的点父亲编号二进制第 $i$ 位。
这样求 $\log$ 次即可解决子问题,正解将多个不同深度的点放在一起处理即可(为了是同组点间互不干扰,选择按深度模 $3$ 分组)。
求深度 $4\log$ 次操作,求父亲编号 $3\log$ 次操作,总共 $7\log$ 次,可以通过本题。
#include<cstdio> #include<algorithm> using namespace std; int n,L=1,dis[1001]; int dep[1001],fa[1001]; bool final[1001],have[1001]; void query(void) { putchar('?'); for(int i=1;i<=n;i++) printf(" %d",dis[i]); putchar('\n'); fflush(stdout); for(int i=1;i<=n;i++){ char c; scanf(" %c",&c); have[i]=c-'0'; } return; } void update(int d,int rem,int opt) { for(int i=1;i<=n;i++) if(final[i]&&(dep[i]/d/2)%2==rem) dis[i]=d-opt; else dis[i]=0; query(); for(int i=1;i<=n;i++){ if(final[i]||dep[i]/d/2%2!=rem) continue; if(opt==1&&!have[i]) dep[i]+=d; if(opt==0&&have[i]&&dep[i]/d%2==1) final[i]=1; } return; } bool vis[1001]; void getdep(int lim) { for(int bit=lim>>1;bit>1;bit>>=1){ update(bit,0,1); update(bit,1,1); update(bit,0,0); update(bit,1,0); } for(int i=1;i<=n;i++) if(!final[i]) dep[i]++; return; } void solve(int rem) { for(int w=1;w<=n;w<<=1){ for(int i=1;i<=n;i++) if(i/w%2==1&&dep[i]%3==rem) dis[i]=1; else dis[i]=0; query(); for(int i=1;i<=n;i++) if(dep[i]%3==(rem+1)%3&&have[i]) fa[i]+=w; } return; } int main(void) { scanf("%d",&n); while(L<n) L<<=1; final[1]=1; getdep(L); solve(0); solve(1); solve(2); printf("!\n"); for(int i=2;i<=n;i++) printf("%d %d\n",fa[i],i); fflush(stdout); return 0; }

浙公网安备 33010602011771号