1. 程式人生 > 實用技巧 >AT2377 [AGC014E] Blue and Red Tree

AT2377 [AGC014E] Blue and Red Tree

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; }
Blue and Red Tree