CF1284F New Year and Social Network
阿新 • • 發佈:2021-08-24
一、題目
二、解法
根據樣例大膽猜結論:所有邊都可以被匹配。
證明考慮歸納法,對於 \(\tt T_1\) 的一個葉子 \(x\),找到它的父親 \(y\),在第二棵樹上找到 \((x,y)\) 路徑上連線 \(x\) 的邊 \((x,t)\),把邊 \((x,y)\) 和邊 \((x,t)\) 連線,然後把其他連向 \(x\) 的邊都連向 \(y\),顯然這和原圖等價,那麼兩棵樹的大小都同時縮小 \(1\),那麼就歸納到了更小的情況,所以歸納到最後一定是完美匹配。
考慮模擬上面的過程,但是斷開的邊數量太多直接 \(\tt lct\) 過不了。
優化刪邊的方法其實就是建虛點,這裡我們可以把 \(x\)
三、總結
樹問題:簡單角度考慮(根、葉子、中心\(...\)),歸納法求證。
#include <cstdio> #include <vector> #include <iostream> #include <cassert> using namespace std; const int M = 500005; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,a[M],x[M],y[M],fa[M],d[M];vector<int> g[M]; namespace lct { int fa[M],ch[M][2],sz[M],v[M],fl[M],st[M]; int chk(int x) { return ch[fa[x]][1]==x; } int nrt(int x) { return ch[fa[x]][0]==x || ch[fa[x]][1]==x; } void flip(int x) { if(!x) return ; swap(ch[x][0],ch[x][1]); fl[x]^=1; } void down(int x) { if(!x) return ; if(fl[x]) { flip(ch[x][0]); flip(ch[x][1]); fl[x]=0; } } void up(int x) { sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+v[x]; } void rotate(int x) { int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1]; ch[y][k]=w;fa[w]=y; if(nrt(y)) ch[z][chk(y)]=x;fa[x]=z; ch[x][k^1]=y;fa[y]=x; up(y);up(x); } void splay(int x) { int z=x,t=0;st[++t]=z; while(nrt(z)) z=fa[z],st[++t]=z; while(t) down(st[t--]); while(nrt(x)) { int y=fa[x]; if(nrt(y)) { if(chk(x)==chk(y)) rotate(y); else rotate(x); } rotate(x); } } void access(int x) { for(int y=0;x;x=fa[y=x]) splay(x),ch[x][1]=y,up(x); } void makert(int x) { access(x);splay(x);flip(x); } void link(int x,int y) { makert(x);fa[x]=y; } void cut(int x,int y) { makert(x);access(y);splay(x); ch[x][1]=fa[y]=0;up(x); } void split(int x,int y) { makert(x);access(y);splay(x); } int ask(int x) { down(x); if(sz[ch[x][0]]) return ask(ch[x][0]); if(v[x]) return x; return ask(ch[x][1]); } } void dfs(int u) { for(auto v:g[u]) if(v^fa[u]) fa[v]=u,dfs(v); } signed main() { n=read(); for(int i=1;i<n;i++) { int u=read(),v=read(); g[u].push_back(v); g[v].push_back(u); } for(int i=1;i<n;i++) { x[i]=read();y[i]=read(); lct::v[i+n]=lct::sz[i+n]=1; lct::link(i+n,x[i]); lct::link(i+n,y[i]); } dfs(1); for(int i=1;i<=n;i++) d[fa[i]]++; for(int i=1;i<=n;i++) if(!d[i]) a[++m]=i; printf("%d\n",n-1); for(int i=1;i<n;i++) { int t=a[i];d[fa[t]]--; if(!d[fa[t]]) a[++m]=fa[t]; lct::split(t,fa[t]); int id=lct::ask(t);//the first real edge lct::cut(id,x[id-n]); lct::cut(id,y[id-n]); lct::v[id]=lct::sz[id]=0;//clear lct::link(id,t); lct::link(id,fa[t]); printf("%d %d %d %d\n",t,fa[t],x[id-n],y[id-n]); } }