题意:
给出一颗树,初始每条边是蓝色,可以选择一条全是蓝色的路径,删去其中一条边,在这条路径的两个端点连上一条红色边,现给你两棵树,判断第一棵树能不能变成第二棵树
(n<=1e5)
Solution:
我们只考虑蓝色边,注意到这是一棵树,所以每次删除一条边后会把图分成两部分,分成两部分后就不会出现不同部分的点之间有全是蓝色的路径这种情况,那么我们就可以把这两部分递归来处理了。
考虑处理到最后,同部分内会剩下两个点,那么这两个点在原来的树和最终的树中都有边
那么方法就明了了:我们倒着来进行这种操作,每次选择有两条边的点对,把它们缩起来,判断能不能缩n-1次即可
具体实现可以用map+set
代码:
#include<cstdio>
#include<map>
#include<set>
#include<iostream>
#define mp(a,b) make_pair(a,b)
using namespace std;
int fa[100010],n,x,y;
map<pair<int,int>,int>G,vis;
multiset<int> S[100010];
pair<int,int> q[1000010];
int t,h;
void p(int &x,int &y){
if (x>y) swap(x,y);}
int find(int x)
{
//cout<<x<<endl;
if (fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++) scanf("%d%d",&x,&y),p(x,y),G[mp(x,y)]++,S[x].insert(y),S[y].insert(x);
for (int i=1;i<n;i++) {
scanf("%d%d",&x,&y),p(x,y);S[x].insert(y),S[y].insert(x);if (++G[mp(x,y)]>1) vis[mp(x,y)]=1,q[++t]=mp(x,y);}
for (int i=1;i<=n;i++) fa[i]=i;
for (int k=1;k<n;k++)
{
int x,y;
while (1)
{
if (h==t) {
printf("NO");return 0;}
x=find(q[++h].first),y=find(q[h].second);
if (x==y) continue;
else break;
}
if (S[x].size()>S[y].size()) swap(x,y);
fa[x]=y;
for (set<int>::iterator i=S[x].begin();i!=S[x].end();i++)
{
int nw=find(*i);
if (nw==y) continue;
S[y].insert(nw),S[nw].insert(y);
int xx,yy;
if (nw>y) xx=y,yy=nw;
else xx=nw,yy=y;
if (++G[mp(xx,yy)]>1&&vis[mp(xx,yy)]!=1) vis[mp(xx,yy)]=1,q[++t]=mp(xx,yy);
if (S[nw].find(x)!=S[nw].end()) S[nw].erase(x);
}
S[y].erase(x);
}
printf("YES");
}