1. 程式人生 > 實用技巧 >CF1442E. Black, White and Grey Tree

CF1442E. Black, White and Grey Tree

題目大意

一棵黑白灰的樹,每次選擇一個連通塊內的一個子點集將其刪掉,不能同時刪黑白點,求最少刪完的次數

n<=2e5

題解

不是這DE加起來怎麼還沒有C難寫啊

發現把樹刪成兩個連通塊不比從外往內刪優,因為可以同時刪更多的點

又發現灰色點一定不會先刪,一定是相鄰的點只剩一個時跟著那個點一起刪

轉化一下,先把灰色塊縮成一個點,之後把其相連的黑白點縮成兩個點,最後刪掉灰色點並把合成的黑白兩個點連起來,如果只有某一種顏色就不用連,這樣得到的圖和原圖等價

因為一個灰色點被刪的時間是與其相連的點的最晚被刪時間,所以縮起來之後時間並沒有變

接著再把黑色和白色塊縮起來,變成一棵黑白交替的樹,那麼此時再從葉子一層層往內刪,答案就是直徑/2+1

然後在U群裡看到說直接找直徑就可以了,原理等價於普通樹找直徑,證明用反正法把端點移到直徑端點上

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
//#define file
using namespace std;

int a[400001][2],b[200001],ls[200001],T,n,i,j,k,l,len,mx,mx2;

void New(int x,int y) {++len;a[len][0]=y;a[len][1]=ls[x];ls[x]=len;}
void dfs(int Fa,int t,int Ls,int sum)
{
	int i;
	if (sum>mx) mx=sum,mx2=t;
	
	for (i=ls[t]; i; i=a[i][1])
	if (a[i][0]!=Fa)
	dfs(t,a[i][0],(b[a[i][0]]>0)?b[a[i][0]]:Ls,sum+(b[a[i][0]]>0 && b[a[i][0]]!=Ls));
}

int main()
{
	#ifdef file
	freopen("CF1442E.in","r",stdin);
	#endif
	
	scanf("%d",&T);
	for (;T;--T)
	{
		scanf("%d",&n),len=0,memset(ls,0,(n+1)*4);
		fo(i,1,n) scanf("%d",&b[i]);
		fo(i,1,n-1) scanf("%d%d",&j,&k),New(j,k),New(k,j);
		
		mx=-1,dfs(0,1,b[1],b[1]>0);
		mx=-1,dfs(0,mx2,b[mx2],b[mx2]>0);
		printf("%d\n",mx/2+1);
	}
	
	fclose(stdin);
	fclose(stdout);
	return 0;
}