1. 程式人生 > >hdu6035 Colorful Tree 樹形dp 給定一棵樹,每個節點有一個顏色值。定義每條路徑的值為經過的節點的不同顏色數。求所有路徑的值和。

hdu6035 Colorful Tree 樹形dp 給定一棵樹,每個節點有一個顏色值。定義每條路徑的值為經過的節點的不同顏色數。求所有路徑的值和。

void 題意 iostream cnblogs 編號 emp php scanf http

/**
題目:hdu6035 Colorful Tree
鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6035
題意:給定一棵樹,每個節點有一個顏色值。定義每條路徑的值為經過的節點的不同顏色數。求所有路徑的值和。

思路:看題解後,才想出來的。樹形dp。

求所有路徑的值和 = 路徑條數*總顏色數(n*(n-1)*colors/2)-sigma(每種顏色沒有經過的路徑條數)

主要是求每種顏色沒有經過的路徑條數。

畫一棵樹,我直接用顏色值表示節點編號。

             2
           /             3     4
         /     /          1     3    2
       / \   / \  /       4   5  4  5 3  5        12個點。

首先求顏色值為3的不經過的路徑條數x
樹上有三個3.很容易想到:
x = 最左邊那個3下面的3個點構成的路徑條數(3*2/2=3)+中間的3的兩個子樹分別構成的路徑條數和(0)
+最右邊的3的子樹的分別構成的路徑條數和(0)
+(總節點數-所有的3為根的子樹節點數之和)*(總節點數-所有的以3為根的子樹節點數之和-1)/2 ;

所以size[i]表示以i為根的樹的節點數。

sum[i]在dfs過程中,,維護。。比如假設顏色為2.上圖。 那麽左子樹是根為3,右子樹是根為4.
那麽遞歸完左子樹之後,sum[2] = 0; 然後再遞歸完右子樹後sum[2] = 3;就是右下角的那個2為根的子樹的點數。

最終sum[i]表示所有以i顏色為根的子樹的所有節點數之和。
sum[2] = 12;
sum[1] = 3;
sum[4] = 8;
sum[5] = 3;
sum[3] = 8;


*/ #include<iostream> #include<cstdio> #include<algorithm> #include<vector> #include<cstring> using namespace std; typedef long long LL; const int N = 2e5+100; int size[N]; int sum[N]; int col[N]; int vis[N]; int colors, n; LL cnt; vector<int> G[N]; void dfs(int r,int
f) { int len = G[r].size(), temp = 0; size[r] = 1; if(sum[col[r]]!=0){ temp = sum[col[r]]; sum[col[r]] = 0; } for(int i = 0; i < len; i++){ int to = G[r][i]; if(to==f) continue; dfs(to,r); size[r] += size[to]; cnt += (LL)(size[to]-sum[col[r]])*(size[to]-sum[col[r]]-1
)/2; sum[col[r]] = 0; } sum[col[r]] = size[r]+temp; } int main() { int cas = 1; while(scanf("%d",&n)==1) { memset(vis, 0, sizeof vis); memset(size, 0, sizeof size); memset(sum, 0, sizeof sum); colors = 0; for(int i = 1; i <= n; i++) G[i].clear(); for(int i = 1; i <= n; i++){ scanf("%d",&col[i]); if(vis[col[i]]==0){ colors++; } vis[col[i]] = 1; } int u, v; for(int i = 1; i <= n-1; i++){ scanf("%d%d",&u,&v); G[u].push_back(v); G[v].push_back(u); } cnt = 0; dfs(1,-1); for(int i = 1; i <= n; i++){ if(vis[i]==0) continue; cnt += (LL)(n-sum[i])*(n-sum[i]-1)/2; } printf("Case #%d: %lld\n",cas++,(LL)n*(n-1)/2*colors-cnt); } return 0; }

hdu6035 Colorful Tree 樹形dp 給定一棵樹,每個節點有一個顏色值。定義每條路徑的值為經過的節點的不同顏色數。求所有路徑的值和。