1. 程式人生 > 其它 >CF1284F New Year and Social Network

CF1284F New Year and Social Network

一、題目

點此看題

二、解法

根據樣例大膽猜結論:所有邊都可以被匹配

證明考慮歸納法,對於 \(\tt T_1\) 的一個葉子 \(x\),找到它的父親 \(y\),在第二棵樹上找到 \((x,y)\) 路徑上連線 \(x\) 的邊 \((x,t)\),把邊 \((x,y)\) 和邊 \((x,t)\) 連線,然後把其他連向 \(x\) 的邊都連向 \(y\),顯然這和原圖等價,那麼兩棵樹的大小都同時縮小 \(1\),那麼就歸納到了更小的情況,所以歸納到最後一定是完美匹配。

考慮模擬上面的過程,但是斷開的邊數量太多直接 \(\tt lct\) 過不了。

優化刪邊的方法其實就是建虛點,這裡我們可以把 \(x\)

當作虛點,\((x,y)\) 之間連一條假邊,在匹配的時候我們需要找到 \((x,y)\) 之間第一條真邊,時間複雜度 \(O(n\log n)\)

三、總結

樹問題:簡單角度考慮(根、葉子、中心\(...\)),歸納法求證。

#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]);
	}
}