zoukankan      html  css  js  c++  java
  • CF1284F New Year and Social Network

    题面
    题目大意:给定两棵树T1和T2,我们称T1中的一条边(e)和T2中的一条边(f)匹配
    当且仅当:T1-(e)+(f)是棵树。
    求这个二分图的最大匹配。
    (n)<=2.5e5

    题解:
    先大力猜一波结论:这个二分图存在完美匹配。
    证明:
    首先需要证明这么一个东西:
    在T1中任选一条不与T2的边重合的边(e),一定存在T2中的与T1的边不重合的边(f)满足T1-(e)+(f)和T2-(f)+(e)都是一棵树。
    首先将(e)加到T2上,那么就形成了一个环。我们要选的(f)边就需要在这个环中选出。
    我们将环里的每个点编号为1或2,表示它们分别属于T1-(e)的两个连通块。
    那么由于e两边的编号不同,这个环中就永远存在一条1-2的边(f),联通T1。
    由此,我们得到了一个O((n^2))的做法。
    考虑如何缩减时间复杂度。
    因为我们的构造是在改动T2,而T1不能变。而这些找边的操作很难在小于线性的时间内作出。
    不难想到选定T2中的边,找T1中满足条件的点。至于如何维护T1的边是否可用,可以拿并查集来维护。
    考虑从T2的叶子结点开始考虑。设当前的叶子结点为(u),这条边连向的点是(v)
    我们求出(u),(v)在T1中的(lca)(u)所在并查集(连通块)的深度最小的节点(a)
    (dep[a]>dep[lca]),我们就选择a和它的father这条边连起来;否则,通过倍增找出(v)(lca)上的一条未使用的且深度最小的边,连起来。
    如此操作n-1次即可。
    时间复杂度:(O(nlogn))

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define re register int
    #define F(x,y,z) for(re x=y;x<=z;x++)
    #define FOR(x,y,z) for(re x=y;x>=z;x--)
    typedef long long ll;
    #define I inline void
    #define IN inline int
    #define C(x,y) memset(x,y,sizeof(x))
    #define STS system("pause")
    template<class D>I read(D &res){
    	res=0;register D g=1;register char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')g=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch)){
    		res=(res<<3)+(res<<1)+(ch^48);
    		ch=getchar();
    	}
    	res*=g;
    }
    vector<int>e[303000],t[303000];
    int n,m,X,Y,lg[303000],dep[303000],f[303000][21],du[303000],ma[303000],vis[303000];
    queue<int>q;
    I D_1(int x,int fa,int depth){
    	dep[x]=depth;f[x][0]=fa;
    	F(i,1,lg[dep[x]])f[x][i]=f[f[x][i-1]][i-1];
    	for(auto d:e[x]){
    		if(d==fa)continue;
    		D_1(d,x,depth+1);
    	}
    }
    IN ques_lca(int x,int y){
    	if(dep[x]>dep[y])swap(x,y);
    	re det=dep[y]-dep[x];
    	F(i,0,lg[det])if((det>>i)&1)y=f[y][i];
    	if(x==y)return x;
    	FOR(i,17,0){
    		if(f[x][i]&&f[y][i]&&f[x][i]^f[y][i])x=f[x][i],y=f[y][i];
    	}
    	return f[x][0];
    }
    IN find(int x){return ma[x]==x?x:ma[x]=find(ma[x]);}
    I merge(int x,int y){
    	x=find(x);y=find(y);if(x==y)return;ma[x]=y;
    }
    int main(){
    	read(n);
    	F(i,1,n-1){read(X);read(Y);e[X].emplace_back(Y);e[Y].emplace_back(X);}
    	F(i,1,n-1){read(X);read(Y);t[X].emplace_back(Y);t[Y].emplace_back(X);du[X]++;du[Y]++;}
    	lg[0]=-1;F(i,1,n)lg[i]=lg[i>>1]+1,ma[i]=i;
    	D_1(1,0,1);
    	F(i,1,n)if(du[i]==1)q.emplace(i);
    	printf("%d
    ",n-1);
    	while(q.size()>1){
    		re u=q.front(),v;q.pop();vis[u]=1;
    		for(auto d:t[u])if(!vis[d]){
    			v=d;du[d]--;
    			if(du[d]==1)q.emplace(d);
    			break;
    		}
    		re lca=ques_lca(u,v),a,b;
    		a=find(u);
    		if(dep[a]>dep[lca]){
    			b=f[a][0];printf("%d %d %d %d
    ",a,b,u,v);
    			ma[a]=find(b);
    		}
    		else{
    			b=v;
    			FOR(i,17,0)if(dep[f[b][i]]>dep[lca]&&find(f[b][i])!=a)b=f[b][i];
    			printf("%d %d %d %d
    ",b,f[b][0],u,v);
    			ma[b]=a;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    『重构--改善既有代码的设计』读书笔记----Replace Temp with Query
    LXPanel自定义添加应用程序到快速启动栏
    『重构--改善既有代码的设计』读书笔记----Inline Temp
    『重构--改善既有代码的设计』读书笔记----Inline Method
    App Store自动下载WiFi与蜂窝数据切换机制
    『重构--改善既有代码的设计』读书笔记----Extract Method
    EXpression 表达式目录树
    linq to sql and linq to object 总结
    基于反射实现实体DTO映射
    对文件操作
  • 原文地址:https://www.cnblogs.com/Purple-wzy/p/13184955.html
Copyright © 2011-2022 走看看