题面
题目大意:给定两棵树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
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")
templateI 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;
}
vectore[303000],t[303000];
int n,m,X,Y,lg[303000],dep[303000],f[303000][21],du[303000],ma[303000],vis[303000];
queueq;
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",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\n",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\n",b,f[b][0],u,v);
ma[b]=a;
}
}
return 0;
}