• CF566E Restoring Map 解题报告


    有一棵 (n) 个节点的树,只给出每个点与其距离不超过 (2) 的点的集合(包含自己),要求构造出一棵符合要求的树,保证有解。

    (nle 1000)

    思维不难,但是细节很多,不愧是评分 3200 的题(也可能是我的方法太怪了)。

    一个显然的方法就是减小数据大小递归求解,考虑如何找出树的某些叶子,可以发现,如果存在 (x) 个集合一样且这些集合的大小为 (x+2) ,那么这些集合对应的点就是一堆叶子,如果只看这些叶子和它们的父亲就是一个菊花图,显然任何形态的树都存在这一结构,如果能找到另外一个集合和这种集合的交的大小为 (2) ,显然在交中的点不是叶子,这就意味着,当目前的树的高度大于 (2) 时,我们一定可以靠这种方法找出这一结构并找出叶子,然后将可以减小规模递归求解。

    当树的高度小于等于 (2) 时,那么目前的树是一个菊花图,剩下的所有集合都应该是相同的,可以发现,如果有一个原集合和现在的集合交的大小为 (1) ,那么在交中的点一定不是根,如果交的大小为 (2) ,那么在交中的点一定包含了根,然后枚举一遍做交就可以找出根了。

    然后从根往回递归,记得存之前找出的叶子的父亲和爷爷节点,根据已经形成的树判断那两个点哪个为父亲哪个为爷爷。

    找叶子时需要找到出一个交为 (2) 的点,可以预处理出哪两个集合交为 (2) ,判断交要用 bitset 优化,复杂度可降为 (mathcal{O(frac{n^3}{w})})

    判断两个集合是否相同可以用随机化 hash ,对每个点打上一个 ([1,2^{64}]) 的随机数,然后一个集合的 hash 值就是集合的点的随机数的异或和,删除一个点就再异或这个点的随机数,可以发现出锅概率大概是 (frac{1}{2^{64}})

    需要注意的细节:

    1. 要对 (n=2) 特判。

    2. 每次找叶子就只找同一个父亲的叶子删除,不然连样例 2 都过不了(其实是我的方法有一点小锅,但是我还没有想到怎么样才能解决)。

    其实我一开始找叶子的方法不是这样的,后来假了三四次才找到了正确方法,所以做题时一定要想清楚了再写啊qaq(但这并不妨碍我骂出题人)。

    MDFK = 莫队分块(确信)

    已经去掉了 3K 的调试代码后的代码:

    #include<bits/stdc++.h>
    using namespace std;
    
    #define ll long long
    const int M=1005;
    
    int read(){
    	int x=0,y=1;char ch=getchar();
    	while(ch<'0'||ch>'9') y=(ch=='-')?-1:1,ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return x*y;
    }
    
    ll RAND(){
    	ll res=0;
    	for(int i=1;i<=17;i++) res=res*10ll+rand()%10;
    	return res;
    }
    
    int tot=0,first[M];
    struct Edge{ int nxt,to; }e[M*M];
    void add(int x,int y){
    	e[++tot]=(Edge){first[x],y};
    	first[x]=tot;
    }
    
    ll IH[M],Hash[M];
    int n,sy,SZ[M],fa[M][2];bool vis1[M],vis2[M],vis3[M];
    vector<int> cun[M],hhh[M];bitset<M> jh[M],fuc[M],fz;
    struct FUCK{ ll h;int id; }FFF[M];
    bool cmp(FUCK x,FUCK y){ return x.h<y.h; }
    int MDFK[M];
    void work(int cs){
    	int cnt=0,CNT=0;
    	for(int i=1;i<=n;i++) if(!vis1[i]) FFF[++CNT]=(FUCK){Hash[i],i};
    	sort(FFF+1,FFF+CNT+1,cmp);
    	for(int i=1;i<=CNT;i++){
    		int j=i;
    		while(j<CNT&&FFF[j+1].h==FFF[i].h) j++;
    		if(j-i+1==SZ[FFF[i].id]-2) for(int k=i;k<=j;k++) MDFK[++cnt]=FFF[k].id;
    		i=j;
    	}
    	int minn=1e9,u=1;
    	for(int i=1;i<=cnt;i++){
    		if(Hash[MDFK[i]]!=Hash[MDFK[i+1]]||i==cnt){
    			if(SZ[MDFK[i]]<minn) minn=SZ[MDFK[i]],u=i;
    		}
    	}
    	int fzcnt=0;ll NH=Hash[MDFK[u]];
    	for(int i=1;i<=cnt;i++) if(Hash[MDFK[i]]==NH) MDFK[++fzcnt]=MDFK[i];
    	cnt=fzcnt;
    	if(!cnt){
    		int rt=1,u;
    		for(int i=1;i<=n;i++) if(!vis1[i]) u=i;
    		for(int i=1;i<=n;i++){
    			if(!vis1[i]) continue ;
    			fz=fuc[i]&jh[u];
    			if(fz.count()==1) for(int j=1;j<=n;j++) if(fz[j]) vis3[j]=1;
    			if(fz.count()==2) for(int j=1;j<=n;j++) if(!fz[j]) vis3[j]=1;
    		}
    		for(int i=1;i<=n;i++) if(!vis2[i]&&!vis3[i]) rt=i;
    		for(int i=1;i<=n;i++) if(!vis2[i]&&i!=rt) printf("%d %d
    ",fa[i][0]=rt,i);
    		return ;
    	}
    	for(int l=1;l<=cnt;l++){
    		int i=MDFK[l],u;
    		for(int j=first[i];j;j=e[j].nxt){
    			int v=e[j].to;
    			if(!vis1[v]) u=v;
    		}
    		fz=fuc[i]&fuc[u];
    		int x=fz._Find_first(),y=fz._Find_next(x);
    		for(int j=1;j<=n;j++){
    			if(jh[i][j]&&j!=x&&j!=y){
    				if(vis2[j]) continue ;
    				vis2[j]=1;hhh[cs].push_back(j);
    				fa[j][0]=x,fa[j][1]=y;
    				int sz=cun[j].size();
    				for(int k=0;k<sz;k++){
    					int v=cun[j][k];
    					jh[v][j]=0;Hash[v]^=IH[j];SZ[v]--;
    				}
    			}
    		}
    	}
    	for(int i=1;i<=cnt;i++) vis1[MDFK[i]]=1;
    	work(cs+1);
    	int FUKC=hhh[cs].size();
    	for(int i=0;i<FUKC;i++){
    		int j=hhh[cs][i];
    		if(fa[fa[j][1]][0]==fa[j][0]) swap(fa[j][0],fa[j][1]);
    		printf("%d %d
    ",fa[j][0],j);
    	}
    }
    void solve(){
    	sy=n=read();
    	for(int i=1;i<=n;i++) IH[i]=RAND();
    	for(int i=1;i<=n;i++){
    		int sz=read(),x;
    		for(int j=1;j<=sz;j++) x=read(),jh[i][x]=fuc[i][x]=1,cun[x].push_back(i),Hash[i]^=IH[x];
    	}
    	if(n==2) return (void)(printf("1 2
    "));
    	for(int i=1;i<=n;i++)
    		for(int j=i;j<=n;j++){
    			int x=(jh[i]&jh[j]).count();
    			if(i==j) SZ[i]=x;
    			else if(x==2) add(i,j),add(j,i);
    		}
    	work(0);
    }
    
    signed main(){
    	srand(time(NULL));
    	solve();
    }
    
  • 相关阅读:
    React学习笔记
    士兵杀敌(一)
    网络爬虫Java实现抓取网页内容
    德莱联盟
    计算球体积
    在页面上 获取键盘事件
    一起玩转mysql
    腾讯云 做出的 不错的 动画 chrome小插件
    jquery MD5
    json to xml
  • 原文地址:https://www.cnblogs.com/wzp-blog/p/14371384.html
Copyright © 2020-2023  润新知