CF 600E 树上启发式合并
Posted lesning
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF 600E 树上启发式合并相关的知识,希望对你有一定的参考价值。
DUS on tree
难得都不会,会的都是板子,可悲,可悲
题意:略
先想一个O(n^2)的写法,然后想办法去掉重复计算。究竟哪里重复 了呢?
假设p是x的儿子,p有很多个。每次计算答案的时候,如果“重儿子”(子孙最多的p)的答案可以直接用的话,
就可以省去很多的重复计算,这就是书上启发式合并
DUS ON TREE写法类似板子(恕我无知,没见过其他整法)
#include<cstdio> #include<vector> #include<algorithm> #include<iostream> #include<set> using namespace std; typedef long long ll; const int maxn = 1e5 + 7; ll list[maxn]; ll ans, mx; int siz[maxn];//根 int son[maxn];//重儿子 ll cns[maxn]; vector<int>G[maxn]; void insert(int be, int en) { G[be].push_back(en); } int dfs(int x, int fa) { siz[x] = 1; int c = 0; for(int p : G[x]) { if (p == fa) continue; dfs(p, x); siz[x] += siz[p]; if (c < siz[p]) { c = siz[p]; son[x] = p; } } return 0; } ll cnt[maxn];//出现次数 int cal(int x, int fa, int cn, int ban) { cnt[list[x]] += cn; if (cnt[list[x]] > mx) { mx = cnt[list[x]];//times ans = list[x]; } else if (cnt[list[x]] == mx) { ans += list[x]; } for (int p : G[x]) { if (p == fa || p == ban) continue; cal(p, x, cn, ban); } return 0; } int dfs2(int x, int fa,int f) { for (int p : G[x]) { if (p == fa || p == son[x]) continue; dfs2(p, x, 1); } if (son[x] ) { dfs2(son[x], x, 0); } cal(x, fa, 1, son[x]); cns[x] = ans; if (f == 1) { cal(x, fa, -1, 0);//把树清理干净 ans = 0; mx = 0; } return 0; } int main() { int n; int be, en; ans = 0; mx = 0; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%lld", &list[i]); } for (int i = 1; i < n; i++) { scanf("%d %d", &be, &en); insert(be, en); insert(en, be); } dfs(1, -1); dfs2(1, -1, 0); for (int i = 1; i <= n; i++) { if (i == 1) printf("%lld", cns[i]); else printf(" %lld", cns[i]); } printf(" "); return 0; }
以上是关于CF 600E 树上启发式合并的主要内容,如果未能解决你的问题,请参考以下文章