题目链接:http://codeforces.com/contest/600/problem/E
给你一棵树,告诉你每个节点的颜色,问你以每个节点为根的子树中出现颜色次数最多的颜色编号和是多少。
最容易想到的是n*n的暴力,但是会超时。所以这里用到类似并查集的合并,对于每个子树颜色种数少的合并到颜色种数大的当中。
不懂的看代码应该可以明白。
1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio> 7 #include <vector> 8 #include <cmath> 9 #include <ctime> 10 #include <list> 11 #include <set> 12 #include <map> 13 using namespace std; 14 typedef long long LL; 15 typedef pair <int, int> P; 16 const int N = 1e5 + 5; 17 map <int, int> cnt[N]; //first代表颜色编号,second代表出现次数 18 map <int, LL> sum[N]; //first代表出现次数,second代表颜色编号之和 19 LL ans[N]; 20 int head[N], tot, a[N]; 21 struct Edge { 22 int next, to; 23 }edge[N << 1]; 24 25 void init() { 26 memset(head, -1, sizeof(head)); 27 } 28 29 inline void add(int u, int v) { 30 edge[tot].next = head[u]; 31 edge[tot].to = v; 32 head[u] = tot++; 33 } 34 35 void dfs(int u, int p) { 36 cnt[u][a[u]] = 1; //初始化:a[u]颜色出现一次 37 sum[u][1] = (LL)a[u]; //初始化:出现一次的颜色之和为a[u] 38 for(int i = head[u]; ~i; i = edge[i].next) { 39 int v = edge[i].to; 40 if(v == p) 41 continue; 42 dfs(v, u); 43 if(cnt[u].size() < cnt[v].size()) { //颜色种类少的合并到颜色种类多的,u为颜色种类多的子树 44 swap(cnt[u], cnt[v]); 45 swap(sum[u], sum[v]); 46 } 47 for(auto it: cnt[v]) { 48 cnt[u][it.first] += it.second; //it.first颜色出现次数合并累加 49 int temp = cnt[u][it.first]; //temp为it.first颜色次数 50 sum[u][temp] += (LL)it.first; //累加出现temp次的颜色 51 } 52 cnt[v].clear(); //清空 53 sum[v].clear(); 54 } 55 ans[u] = sum[u].rbegin()->second; //最大出现次数的颜色之和 56 } 57 58 int main() 59 { 60 init(); 61 int n, u, v; 62 scanf("%d", &n); 63 for(int i = 1; i <= n; ++i) { 64 scanf("%d", a + i); 65 } 66 for(int i = 1; i < n; ++i) { 67 scanf("%d %d", &u, &v); 68 add(u, v); 69 add(v, u); 70 } 71 dfs(1, -1); 72 for(int i = 1; i <= n; ++i) { 73 printf("%lld%c", ans[i], i == n? ' ': ' '); 74 } 75 return 0; 76 }