好久好久之前,有一棵樹加入了 UOJ 羣。ios
這天,在它討論「一棵樹應該怎麼旋轉」的時候一不當心被刪除了,變成了被刪除的樹。spa
忽然間,它忽然發現它失去了顏色,變成了一棵純白的樹。這讓它感受很焦躁,因而它來拜託你給本身染上一些顏色。code
咱們能夠把它描述成一棵n個節點的有根樹(默認樹的根爲1號節點),全部非根的度數爲1的節點被稱爲葉子節點。最開始全部的點都是白色的。blog
如今你須要選出一些節點並把這些節點染成黑色的。爲了迎合樹的審美,你的染色方案必需要知足全部葉子節點到根路徑上的黑色節點個數相同。遞歸
你發現黑色節點個數越多,樹就會越高興,因此你想要知道在全部合法的染色方案中,黑色節點總個數最可能是多少。string
神題。(感受UOJ Round的題全是神題)it
$O(n^2)$的DP你們都會,可是跟正解並無什麼關係;io
顯然題目要求至關於使白色節點最少;class
有幾個結論:stream
1.若是一種合法方案中根節點到全部葉節點的路徑上都通過白色節點,那麼dfs一遍這棵樹,在遇到白色節點時染黑並回溯,這樣一定能夠使得白色節點變少或不變且依然合法;
2.若是一種合法方案中根節點到深度最淺的葉節點的路徑上通過白色節點,那麼根節點到全部葉節點的路徑上一定都通過白色節點;假定根節點深度爲1,由於其餘葉節點的深度不小於到最淺葉節點的深度,而根節點到其餘葉節點路徑上的黑節點數等於到最淺葉節點路徑上的黑節點數,而小於他們的深度,所以根節點到其餘葉節點的路徑上都會通過至少一個白點;
綜上,一種最優的合法方案一定知足根節點到最淺葉節點的路徑上沒有白點;
這樣一種構造方法就是先把根節點到最淺葉節點的路徑所有染黑,而後遞歸判斷根節點的其餘子樹跟是否要染白再往下作便可;
簡化到每一個點,實際上一個點被染白當且僅當該節點子樹中最淺葉節點的深度>整棵樹的最淺葉節點深度+該節點到根節點路徑上的白點個數;
預處理每一個點子樹中的最淺葉節點深度,再一次dfs判斷便可,時間複雜度$O(n)$。
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #define inf 2147483647
8 #define eps 1e-9
9 using namespace std; 10 typedef long long ll; 11 typedef double db; 12 struct edge{ 13 int v,next; 14 }a[200001]; 15 int n,u,v,ans=0,tot=0,du[100001],mid[100001],head[100001]; 16 void add(int u,int v){ 17 a[++tot].v=v; 18 a[tot].next=head[u]; 19 head[u]=tot; 20 } 21 void dfs(int u,int fa,int dpt){ 22 if(u!=1&&du[u]==1)mid[u]=dpt; 23 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 24 int v=a[tmp].v; 25 if(v!=fa){ 26 dfs(v,u,dpt+1); 27 mid[u]=min(mid[u],mid[v]); 28 } 29 } 30 } 31 void _dfs(int u,int fa,int nwd){ 32 if(nwd<mid[u]){ 33 nwd++; 34 ans++; 35 } 36 for(int tmp=head[u];tmp!=-1;tmp=a[tmp].next){ 37 int v=a[tmp].v; 38 if(v!=fa){ 39 _dfs(v,u,nwd); 40 } 41 } 42 } 43 int main(){ 44 memset(head,-1,sizeof(head)); 45 memset(mid,0x7f,sizeof(mid)); 46 memset(du,0,sizeof(du)); 47 scanf("%d",&n); 48 for(int i=1;i<n;i++){ 49 scanf("%d%d",&u,&v); 50 add(u,v); 51 add(v,u); 52 du[u]++,du[v]++; 53 } 54 dfs(1,0,1); 55 _dfs(1,0,mid[1]); 56 printf("%d",n-ans); 57 return 0; 58 }