【UNR #6 E】神隐(交互)(二进制分组)

神隐

题目链接:UNR #6 E

题目大意

有一棵树,你每次询问可以给边(你不知道对于的两个点)选 0 或 1,然后它会把 0 的边删掉,把得到的每个连通块给你。
要你在不超过 logn+2 次得到每条边。

思路

考虑一个叫做二进制分组的东西,就是我们把读入的边自己编号。
然后每次问第 \(i\) 位是 \(0\) 和是 \(1\) 的分别问一次(就是把这些边标 \(1\) 其它标 \(0\)

那如果两个点恰好在其中的 \(\left\lceil\log_2 n\right\rceil\) 个连通块中,那就是树上的一条边。

然后这样是 \(2\log\) 级别的,考虑优化。
考虑如何才能区分两条边,那就是存在一次询问 A 在 B 不在,一次 B 在 A 不在。
然后我们考虑这么一个方法,就是我们把 \(0\sim 2^T\)\(T\) 是询问次数,下同)里面 \(1\) 的个数为 \(T/2\) 的给找出来。
(其实你找任意个数都可以,但是我们要最大化,避免不能对应所有的边)

然后这个大小是 \(\binom{T}{T/2}\),然后我们会发现刚好大于 \(n-1\)
那我们给每条边编一个上面的数的号,然后就也是二进制分组,但是每次只问 \(1\)
然后由于你限制了 \(1\) 的个数,而且这些数的编号都是不同的,所以一定会有不同的地方,再加上 \(1\) 个数一样,所以一定是各自只要有一个位置自己是 \(1\) 别人是 \(0\)

考虑怎么确定边。
发现如果一个点是叶子点,那它一定会在恰好 \(T/2\) 个询问中为单独一个点一个连通块。
然后考虑删掉这个叶子会怎样,那就把每个它所在的连通块大小减一,那如果减到了 \(1\),我们就可以找到新的叶子节点(其实就是它的父亲)。

所以如果我们要找边,我们删一个点之间,就把那个点集枚举,找到不是它,而且没有确定父亲的点给找出来,那它们的父亲就是你删的点。

代码

#include"tree.h"
#include<iostream>
#include<cassert>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>

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;
}
posted @ 2022-08-12 16:06  あおいSakura  阅读(46)  评论(0编辑  收藏  举报