Description
$ N $ 頂点からなる木があり、頂点には $ 1 $ から $ N $ の番号がついています。 また、 $ N-1 $ 本の辺の内、 $ i $ 番目の辺は頂点 $ a_i $ と頂点 $ b_i $ を結んでいます。
はじめ、各辺は青色に塗られています。 そこで、高橋君は以下の操作を $ N-1 $ 回行い、赤色の木に作り替えることにしました。
- 青色の辺のみからなるパスを一つ選び、そのパス上の辺を一つ取り除く。
- その後、初めに選んだパスの両端点間に赤色の辺を追加する。
最終的に、各 $ i $ に対し、頂点 $ c_i $ と頂点 $ d_i $ を結ぶ赤い辺が存在するような $ N $ 頂点の木に作り替えたいです。
これが可能であるかどうか判定してください。
Solution
新树的每条边在原树上覆盖,最后一次替换的一定是覆盖次数为1的,之前几次以此类推
发现最后一次替换一定是新树上有的边,于是用set保存边集信息,在原树上做dsu on tree
维护边集信息和一个并查集
如果能将所有的点都缩起来说明可行
#include<iostream> #include<utility> #include<cstdio> #include<queue> #include<set> #include<map> using namespace std; int n,fa[100005],ans; queue<pair<int,int> >q; set<int>s[100005]; map<pair<int,int>,int>mp; inline int read() { int f=1,w=0; char ch=0; while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();} while(ch>='0'&&ch<='9') w=(w<<1)+(w<<3)+ch-'0',ch=getchar(); return f*w; } int find(int x) { return (fa[x]==x)?fa[x]:fa[x]=find(fa[x]); } int main() { n=read(); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<n;i++) { int u=read(),v=read(); s[u].insert(v),s[v].insert(u); mp[make_pair(u,v)]++,mp[make_pair(v,u)]++; } for(int i=1;i<n;i++) { int u=read(),v=read(); s[u].insert(v),s[v].insert(u); mp[make_pair(u,v)]++,mp[make_pair(v,u)]++; if(mp[make_pair(u,v)]==2) q.push(make_pair(u,v)); } while(q.size()) { int u=find(q.front().first),v=find(q.front().second); q.pop(); s[u].erase(v),s[v].erase(u); if(s[u].size()>s[v].size()) swap(u,v); for(set<int>::iterator it=s[u].begin();it!=s[u].end();it++) { mp[make_pair(u,*it)]=mp[make_pair(*it,u)]=0; mp[make_pair(v,*it)]++,mp[make_pair(*it,v)]++; if(mp[make_pair(v,*it)]==2) q.push(make_pair(v,*it)); s[v].insert(*it),s[*it].insert(v),s[*it].erase(u); } s[u].clear(),fa[u]=v; } for(int i=1;i<=n;i++) if(fa[i]==i) ++ans; if(ans==1) puts("YES"); else puts("NO"); return 0; }