题解 [UNR #6] 神隐

以前只知道有这么个套路但不理解为什么要这么做,一直以为是个人类智慧构造
更详细的解释
考虑我们要还原的信息是树的形态
考虑对于任意两条相邻的边 \(e_1=(u, v), e_2=(u, w)\),能得到具体连边方式的条件是:
- 存在一组询问使 \(e_1\in E, e_2\notin E\)
- 存在一组询问使 \(e_2\in E, e_1\notin E\)
少一个则无法确定第三个点与确定的两个点之间的连边关系
而由于我们不知道边之间的相邻关系,所以需要得到所有 \(O(n^2)\) 组边对之间的这个信息
考虑一个构造:为每条边赋一个恰有 10 个为 1 位的 20 位二进制权值
进行 20 次询问,每次询问时询问所有权值的这一位为 1 的边
发现一定有且仅有相邻的点对有 10 次出现在同一连通块里
于是有了一个 \(O(n^2\log n)\) 的做法
然后优化上面的做法:
不能枚举点对的话,需要换一种方法找点之间的位置关系
考虑还原树形态的一种常用方法:剥叶子
叶子是在恰好 10 次询问中处于孤立连通块中的点
递归这一过程可以得到原树的拓扑序
为什么不能直接得到连边关系?

对于本题,每个点的具体深度并不可知,但知道所有有祖先关系的点间的相对深度,足以完成上述过程
复杂度 \(O(n\log^2 n)\),因为我用的 set
点击查看代码
#include "tree.h"
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define pb push_back
queue<int> q[2];
int cnt[2010][2010];
vector<int> to[N], mask;
vector<set<int>> gra[25];
vector<vector<int>> com[25];
vector<pair<int, int>> top[25], ans;
vector<vector<int>> query(vector<int> vec);
int tim[N], bel[N][21], flr[N], dep[N], now, rot;
vector<pair<int, int>> force_solve(int n) {
int lim=1<<14;
for (int s=0; s<lim; ++s) if (__builtin_popcount(s)==7) mask.pb(s);
for (int i=0; i<14; ++i) {
vector<int> tem;
for (int j=0; j<n-1; ++j) tem.pb(mask[j]&(1<<i)?1:0);
com[i]=query(tem);
}
for (int i=0; i<14; ++i)
for (auto& it:com[i])
for (auto& s:it)
for (auto& t:it)
++cnt[s][t];
for (int i=0; i<n; ++i)
for (int j=i+1; j<n; ++j)
if (cnt[i][j]==7)
ans.pb({i, j});
return ans;
}
vector<pair<int, int>> solve(int n) {
if (n<=2000) return force_solve(n);
int lim=1<<20;
for (int s=0; s<lim; ++s) if (__builtin_popcount(s)==10) mask.pb(s);
for (int i=0; i<20; ++i) {
vector<int> tem;
for (int j=0; j<n-1; ++j) tem.pb(mask[j]&(1<<i)?1:0);
com[i]=query(tem);
for (int j=0; j<com[i].size(); ++j) {
set<int> s;
for (auto& t:com[i][j]) s.insert(t), bel[t][i]=j;
gra[i].pb(s);
}
}
for (int i=0; i<20; ++i)
for (auto& it:com[i]) if (it.size()==1)
if (++tim[it[0]]==10)
q[now].push(it[0]);
int cnt;
for (lim=1,cnt=0; q[now].size(); ++lim,now^=1) {
// cout<<"lim: "<<lim<<endl;
if (cnt==n-1 && q[now].size()==1) {flr[q[now].front()]=lim; break;}
if (cnt==n-2 && q[now].size()==2) {
flr[q[now].front()]=lim; q[now].pop();
flr[q[now].front()]=++lim; q[now].pop();
break;
}
while (q[now].size()) {
int u=q[now].front(); q[now].pop();
// cout<<"u: "<<u<<endl;
flr[u]=lim; ++cnt;
for (int i=0; i<20; ++i) {
gra[i][bel[u][i]].erase(u);
if (gra[i][bel[u][i]].size()==1 && ++tim[*gra[i][bel[u][i]].begin()]==10)
q[now^1].push(*gra[i][bel[u][i]].begin());
}
}
}
// cout<<"flr: "; for (int i=0; i<n; ++i) cout<<flr[i]<<' '; cout<<endl;
// cout<<"tim: "; for (int i=0; i<n; ++i) cout<<tim[i]<<' '; cout<<endl;
for (int i=0; i<n; ++i) if ((dep[i]=lim-flr[i]+1)==1) rot=i;
for (int i=0; i<20; ++i) {
for (auto& it:com[i]) {
int mdep=INF, mini;
for (auto& t:it) if (dep[t]<mdep) mdep=dep[t], mini=t;
top[i].pb({mdep, mini});
// cout<<"("<<mdep<<','<<mini<<") ";
} //cout<<endl;
}
// cout<<"rot: "<<rot<<endl;
// cout<<"dep: "; for (int i=0; i<n; ++i) cout<<dep[i]<<' '; cout<<endl;
for (int i=0; i<n; ++i) if (i!=rot) {
// cout<<"i: "<<i<<endl;
int mdep=0, maxi;
for (int j=0; j<20; ++j) if (com[j][bel[i][j]].size()>1) {
if (top[j][bel[i][j]].fir>mdep && top[j][bel[i][j]].fir<dep[i])
mdep=top[j][bel[i][j]].fir, maxi=top[j][bel[i][j]].sec; //, cout<<1<<endl;
}
ans.pb({i, maxi});
}
// cout<<"ans: "; for (auto& it:ans) cout<<"("<<it.fir<<','<<it.sec<<") "; cout<<endl;
return ans;
}