• uoj#751-[UNR #6]神隐【交互】


    正题

    题目链接:https://uoj.ac/problem/751


    题目大意

    有一棵 n n n个点的树,你每次可以选择一个边集,交互库会返回你所有联通块,要求这棵树。

    n ≤ 2000 n\leq 2000 n2000,操作次数不超过 14 14 14

    n ≤ 131072 n\leq 131072 n131072,操作次数不超过 20 20 20


    解题思路

    一个神奇的做法,设操作次数为 T T T,我们发现有 C T T 2 ≥ n − 1 C_T^{\frac{T}{2}}\geq n-1 CT2Tn1,我们可以给所有边一个独特的编号 a i a_i ai满足其是一个 T T T位二进制数且其中恰好有 T 2 \frac{T}{2} 2T 1 1 1

    这样对于一个叶子来说它就恰好在 T 2 \frac{T}{2} 2T次询问中是一个单独的连通块,然后我们每次暴力找出叶子删掉即可。

    至于一个叶子的父节点是谁,我们在一个连通块删除的只剩下一个点 x x x时,我们考虑 x x x的儿子,那么在过这个连通块的节点肯定都在 x x x的子树中,此时取出还没有父节点的点,它们的父节点设为 x x x即可。

    时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)


    code

    #include"tree.h"
    #include
    #include
    #include
    using namespace std;
    const int N=1<<17,M=20;
    int T,cnt,a[N],g[N],ans[N],fa[N];
    int r[1<<M],c[M][N],col[M][N];
    vector<vector<int> >ret[M];bool v[N];
    vector<pair<int,int> > e;queue<int> qe;
    vector<pair<int,int> > solve(int n){
    	T=(n<=2000)?14:20;int cnt=0,MS=1<<T;
    	for(int i=0;i<MS;i++)
    		if(__builtin_popcount(i)==T/2){
    			a[cnt]=i;r[i]=cnt++;
    			if(cnt==n-1)break;
    		}
    	vector<int> q;q.resize(n-1);
    	for(int i=0;i<T;i++){
    		for(int j=0;j<n-1;j++)q[j]=((a[j]>>i)&1);
    		ret[i]=query(q);
    		for(int j=0;j<ret[i].size();j++){
    			for(int k=0;k<ret[i][j].size();k++)
    				col[i][ret[i][j][k]]=j;
    			if(ret[i][j].size()==1)g[ret[i][j][0]]++,ans[ret[i][j][0]]|=(1<<i);
    			c[i][j]=ret[i][j].size();
    		}
    	}
    	for(int i=0;i<n;i++)
    		if(g[i]==T/2)qe.push(i),v[i]=1;
    	int x;
    	while(!qe.empty()){
    		x=qe.front();qe.pop();v[x]=1;
    		for(int i=0;i<T;i++){
    			int z=col[i][x];c[i][z]--;
    			if(c[i][z]==1){
    				int y=0,rt=-1;
    				for(int j=0;j<ret[i][z].size();j++){
    					y=ret[i][z][j];
    					if(!v[y]){
    						g[y]++;ans[y]|=(1<<i);
    						if(g[y]==T/2)qe.push(y);
    						rt=y;break;
    					}
    				}
    				if(rt==-1)continue;
    				for(int j=0;j<ret[i][z].size();j++){
    					if(ret[i][z][j]==y||fa[ret[i][z][j]])continue;
    					fa[ret[i][z][j]]=y+1;
    				}
    			}
    		}
    	}
    	MS--;e.resize(n-1);
    	for(int i=0;i<n;i++)
    		if(i!=x)
    			e[r[MS^ans[i]]]=make_pair(fa[i]-1,i);
    	return e;
    }
    
    • 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
  • 相关阅读:
    Redis小而巧的数据库真的很实用,掌握了用起来很舒服
    git工具基本操作命令
    【python基础】复杂数据类型-列表类型(元组)
    vue2和vue3拖拽移动div
    Linux内核详解与内核优化方案
    大模型时代,AI如何成为数实融合的驱动力?
    对象池技术(unity3d)
    UML类图
    武汉星起航跨境——中东电商蓬勃发展,亚马逊中东站点如何发货?
    基于Python_Django的社会实践活动管理系统设计与实现
  • 原文地址:https://blog.csdn.net/Mr_wuyongcong/article/details/126302226