PART1(算法思想简介)
1.实现:
Q:既然轻儿子会被清除掉,还访问它干啥呢 ?好疑惑啊
A:因为这种题的特性是“根和它的子节点的ans依据为包含关系,但是每个节点依旧需要根据其相应的所有依据来作出结论”,所以先访问一次轻子节点是为了得到子节点的ans,而 之后的访问 就和之前 的访问目的(为了得到根的ans)操作 都不一样了,前一次相当于统计数组是tot[v][1~color],之后一次相当于tot[u][1~color],只是数组大小限制 ,不得不使得 u,v共用一个 tot[1~color]
Q:既然这样 ,那先统计重孩子,再统计root,在统计轻子结点可以吗?毕竟root的依据又不依赖轻子节点的依据。
A:不可行,通过“对算法的感悟-从底层思考其起”,就会发现访问重儿子然后统计整棵树后,这课树的所有信息都应到保留下来给它的fa(当它是fa的重孩子时就要留下),所以程序到这里就应当结束了,可是还有轻子结点需要搞,显然是不对的,所以像一个点的轻子结点的数据是肯定不需要保留了(设定是这样,见Q1知道它必定被清楚),先做完,因为它的重孩子做完还有东西要留给它的fa
2.时间复杂度:
3.特别优势:
4.适用情况:
5.需要注意的点:
1.SumDfs的时候只有当前计算的结点的重儿子不重复算,其它轻儿子的各种儿子还是要算啊!!所以SumDfs(u, fa, rootMaxSon)要有rootMaxSon不变
2.遇见 fa必须写成 continue,否则大括号 必须 把 所有人都 框进去,否则你真的 要仔细考虑 了,还是用continue吧
3.图论题也能要用longlong
6.函数、变量名的解释+英文:
dsu on tree(树上合并式启发)(某个毒瘤@noip 说这个是静态链分治)
maxSon要存在才继续dfs啊
7.dalao分析:
第一个例题(关联了好多题)
PART2(算法各种类型(并附上代码))
这是一个例题的代码,不过也算模板了(几百年没遇到要开Longlong的图论题了)

#include<cstdio> #include<cmath> #include<algorithm> #include<set> #include<map> #include<cstring> #include<string> #include<vector> #include<queue> #include<iomanip> #include<iostream>#include<stack> using namespace std; #define inf 0x3f3f3f3f #define ll long long #define re register int const int N = 1e5+10; const int M = 2e5+10; inline int read() { int x=0; char ch=getchar(); while(!isdigit(ch)) ch=getchar(); while(isdigit(ch)) x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return x; } //与边相关 struct edge { int v, next, w; } e[M]; int p[N], eid; inline void InitEdge() { memset(p, -1, sizeof(p)); eid = 0; } inline void Insert(int u, int v, ll w = 0) { e[eid].next = p[u]; e[eid].v = v; e[eid].w = w; p[u] = eid++; } //建图 ll color[N]; inline void In(int &n) { n = read(); for(re i = 1; i <= n; i++) color[i] = read(); int u, v; for(re i = 1; i < n; i++) { u = read(); v = read(); Insert(u, v); Insert(v, u); } } //重链分治 int size[N];//子树大小 int maxSon[N]; void FindMaxSonDfs(int u,int fa) { size[u] = 1; maxSon[u] = 0; for(re i = p[u]; ~i; i = e[i].next) { int v = e[i].v; if(v == fa) continue; FindMaxSonDfs(v, u); size[u] += size[v]; if(size[maxSon[u]] < size[v]) maxSon[u] = v; } } //树上合并式启发 ll ans[N]; ll cnt[N];//记录每种颜色出现的次数 ll maxCnt;//最大的次数 ll sumOfMax;//最大颜色的答案 void SumDfs(int u, int fa, int rootMaxSon) { cnt[color[u]]++; if(cnt[color[u]] > maxCnt) { maxCnt = cnt[color[u]]; sumOfMax = color[u]; } else if(cnt[color[u]] == maxCnt) sumOfMax += color[u]; for(re i = p[u]; ~i; i = e[i].next) { int v = e[i].v; if(v == fa || v == rootMaxSon) continue; SumDfs(v, u, rootMaxSon); } } inline void InitCnt(int u,int fa) //暴力遍历后清空 { cnt[color[u]]--; for(re i = p[u]; ~i; i = e[i].next) { int v = e[i].v; if(v == fa) continue; InitCnt(v, u); } } void Dfs(int u, int fa) { for(re i = p[u]; ~i; i = e[i].next) { int v = e[i].v; if(v == fa || v == maxSon[u]) continue; Dfs(v, u); InitCnt(v, u); maxCnt = sumOfMax = 0; } if(maxSon[u]) { Dfs(maxSon[u], u); } SumDfs(u, fa, maxSon[u]); ans[u] = sumOfMax; } int main() { //freopen("in.txt","r", stdin); //freopen("out.txt","w", stdout); //ios::sync_with_stdio(false); InitEdge(); int n; In(n); FindMaxSonDfs(1, -1); Dfs(1, -1); for(re i = 1; i <= n; i++) printf("%lld ", ans[i]); return 0; }
PART3(算法的延伸应用)
PART4(对算法深度的理解)
DFS在思考时要从最底层想起(不会再继续递归下去的那一层)
PART5(与其相关的有趣题目)