[ARC183D] Keep Perfectly Matched

WrongAnswer_90發表於2024-08-26

My Blogs

[ARC183D] Keep Perfectly Matched

這場不打感覺虧麻了,怎麼大家都不會 D。首先匹配路徑長度之和最大,很典的想到取重心,猜測答案上界 \(\sum_i dep_i\) 可以取到。

取完重心之後,希望不斷把兩個不同的子樹裡的點進行匹配,直到刪空。因為原樹本身存在完美匹配,所以找一對不同子樹裡的點刪去後,根節點的匹配一定變了。

所以選的點一定有一個在根節點當前的匹配點的子樹裡,否則根節點沒有理由更改匹配點。設這個點為 \(x\),則 \(x\) 一定滿足:其到根的路徑上,邊的種類是“匹配邊,非匹配邊,匹配邊...”,即:

image.png

圖中標 \(1\) 的邊是匹配邊,可以發現刪六號點是合法的,而刪 \(8\) 號點的過程中會因為連續出現了兩條非匹配邊而寄掉。

這樣確定了一個子樹中的點,另一個點是可以任意選的。因為要儘量匹配對,所以另一個點應該選在除此之外的 \(siz\) 最大的子樹裡面。接下來根的匹配就是選的第二個子樹中的根節點。繼續做上述過程即可。

這樣做為何能取到最優值:設 \(x\) 是根的初始匹配節點,首先第一次刪點的兩棵子樹一定分別是 \((x,y)\),然後第二次因為此時根和 \(y\) 匹配,所以要刪 \((y,z)\),以此類推,可以發現除了開始的 \(x\) 刪了一個點,剩下的操作都是,選一個子樹刪兩個點,然後跳到另一棵子樹。

除了 \(x\) 子樹大小是奇數,剩下的子樹大小都是偶數,一開始 \(x\) 刪了 \(1\) 就全部變成了偶數。所以不會有奇偶性不對的情況。如果跳到另一棵子樹選擇當前 \(siz\) 最大的,那就一定能夠刪空。因為此時根是樹的重心,每個子樹內需要的操作次數大小都不會超過 \(\frac m 2\),其中 \(m\) 是總操作次數,所以這樣做一定不會爆掉。

現在的問題就是如何高效的找出當前能刪掉的合法點。策略也很簡單:對於點 \(x\) 來說,如果初始他的匹配是他的父親,則他兒子可以按任意順序一個一個刪光。

如果初始他的匹配是他的某個兒子,則先把這個兒子全部刪空時最優的。然後他的匹配就變成了他的父親,他剩下的兒子可以任意排列。

可以 \(\text{dfs}\) 求出每個子樹的後序遍歷,如果有某個兒子和他匹配就優先向這個兒子走,這樣可以求出每個點的合法操作序列。然後套用上述過程,總複雜度 \(\mathcal O(n\log n)\) 或者 \(\mathcal O(n)\)

int n,rt,minn=inf,len,ans[500010],siz[250010];
vi T[250010],ve[250010];
void findrt(int x,int fa=0)
{
	int maxn=0;siz[x]=1;
	for(auto to:T[x])if(to!=fa)
	findrt(to,x),siz[x]+=siz[to],Mmax(maxn,siz[to]);
	if(Mmin(minn,max(n-siz[x],maxn)))rt=x;
}
void dfs(int x,int fa,int top)
{
	ve[top].eb(x);
	for(auto to:T[x])if(to!=fa&&to!=(((x-1)^1)+1))dfs(to,x,top);
	if(fa!=(((x-1)^1)+1))dfs(((x-1)^1)+1,x,top);
}
priority_queue<pii> q;
inline void mian()
{
	read(n);int x,y;pii p;
	for(int i=1;i<n;++i)read(x,y),T[x].eb(y),T[y].eb(x);
	findrt(1),findrt(rt);
	for(auto to:T[rt])dfs(to,rt,to);
	int pos=((rt-1)^1)+1;ans[++len]=ve[pos].back(),ve[pos].pop_back(),--siz[pos];
	for(auto to:T[rt])q.e(mp(siz[to],to));
	for(int I=1;I<(n>>1);++I)
	{
		if(q.top().se==pos)p=q.top(),q.pop();else p=mp(-1,-1);
		pos=q.top().se;
		ans[++len]=ve[pos].back(),ve[pos].pop_back();
		ans[++len]=ve[pos].back(),ve[pos].pop_back();
		siz[pos]-=2;
		q.pop(),q.e(mp(siz[pos],pos));
		if(p.fi!=-1)q.e(p);
	}
	for(auto to:T[rt])if(siz[to])ans[++len]=to;
	ans[++len]=rt;
	for(int i=1;i<=len;i+=2)write(ans[i],' ',ans[i+1],'\n');
}

相關文章