有一棵树,你每次询问可以给边(你不知道对于的两个点)选 0 或 1,然后它会把 0 的边删掉,把得到的每个连通块给你。
要你在不超过 logn+2 次得到每条边。
考虑一个叫做二进制分组的东西,就是我们把读入的边自己编号。
然后每次问第
i
i
i 位是
0
0
0 和是
1
1
1 的分别问一次(就是把这些边标
1
1
1 其它标
0
0
0)
那如果两个点恰好在其中的 ⌈ log 2 n ⌉ \left\lceil\log_2 n\right\rceil ⌈log2n⌉ 个连通块中,那就是树上的一条边。
然后这样是
2
log
2\log
2log 级别的,考虑优化。
考虑如何才能区分两条边,那就是存在一次询问 A 在 B 不在,一次 B 在 A 不在。
然后我们考虑这么一个方法,就是我们把
0
∼
2
T
0\sim 2^T
0∼2T(
T
T
T 是询问次数,下同)里面
1
1
1 的个数为
T
/
2
T/2
T/2 的给找出来。
(其实你找任意个数都可以,但是我们要最大化,避免不能对应所有的边)
然后这个大小是
(
T
T
/
2
)
\binom{T}{T/2}
(T/2T),然后我们会发现刚好大于
n
−
1
n-1
n−1。
那我们给每条边编一个上面的数的号,然后就也是二进制分组,但是每次只问
1
1
1。
然后由于你限制了
1
1
1 的个数,而且这些数的编号都是不同的,所以一定会有不同的地方,再加上
1
1
1 个数一样,所以一定是各自只要有一个位置自己是
1
1
1 别人是
0
0
0。
考虑怎么确定边。
发现如果一个点是叶子点,那它一定会在恰好
T
/
2
T/2
T/2 个询问中为单独一个点一个连通块。
然后考虑删掉这个叶子会怎样,那就把每个它所在的连通块大小减一,那如果减到了
1
1
1,我们就可以找到新的叶子节点(其实就是它的父亲)。
所以如果我们要找边,我们删一个点之间,就把那个点集枚举,找到不是它,而且没有确定父亲的点给找出来,那它们的父亲就是你删的点。
#include"tree.h"
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int N = 131072 + 100;
vector <vector <int> > q[21];
int a[N], sz[21][N], pla[21][N];
queue <int> Q;
int num[N], fa[N];
bool in[N];
vector<pair<int, int> > solve(int n) {
vector <int> xl; int T = (n <= 2000) ? 14 : 20;
for (int i = 0; i < 1 << T; i++) {
int num = 0; for (int j = 0; j < T; j++) if ((i >> j) & 1) num++;
if (num == T / 2) xl.push_back(i);
}
for (int i = 0, now = 0; i < n - 1; i++)
a[i] = xl[now], now = (now + 1) % xl.size();
for (int i = 0; i < T; i++) {
vector <int> as;
for (int j = 0; j < n - 1; j++)
if ((a[j] >> i) & 1) as.push_back(1);
else as.push_back(0);
q[i] = query(as);
for (int j = 0; j < q[i].size(); j++) {
sz[i][j] = q[i][j].size();
for (int k = 0; k < sz[i][j]; k++) pla[i][q[i][j][k]] = j;
if (sz[i][j] == 1) num[q[i][j][0]]++;
}
}
for (int i = 0; i < n; i++) {
fa[i] = -1;
if (num[i] == T / 2) Q.push(i);
}
while (!Q.empty()) {
int now = Q.front(); Q.pop();
for (int i = 0; i < T; i++) { int x = pla[i][now];
if (sz[i][x] == 1) {
for (int j = 0; j < q[i][x].size(); j++) {
if (q[i][x][j] == now) continue;
if (in[q[i][x][j]]) continue;
in[q[i][x][j]] = 1; fa[q[i][x][j]] = now;
}
}
sz[i][x]--;
if (sz[i][x] == 1) {
for (int j = 0; j < q[i][x].size(); j++) {
if (q[i][x][j] == now) continue;
num[q[i][x][j]]++;
if (num[q[i][x][j]] == T / 2) Q.push(q[i][x][j]);
}
}
}
}
vector <pair<int, int> > re;
for (int i = 0; i < n; i++) if (fa[i] != -1) re.push_back(make_pair(i, fa[i]));
return re;
}