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


    神隐

    题目链接:UNR #6 E

    题目大意

    有一棵树,你每次询问可以给边(你不知道对于的两个点)选 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 02T 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 n1
    那我们给每条边编一个上面的数的号,然后就也是二进制分组,但是每次只问 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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
  • 相关阅读:
    【设计模式2_工厂、策略】
    功能:crypto-js加密解密
    【Python基础:面向对象之魔法方法】
    黑马瑞吉外卖之文件上传和下载
    如何看待新东方双语主播董宇辉的走红?
    MySQL业务并发减数量,数量未减
    设计模式:抽象工厂
    RNA修饰技术介绍|介孔二氧化硅纳米颗粒(MSN)搭载的微小RNA-24(miR-24)纳米载体复合物
    PHP反射:探索、修改和实例化
    Truenas scale 配置 TrueChart zerotier
  • 原文地址:https://blog.csdn.net/weixin_43346722/article/details/126304148