模板【树上合并式启发】

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分析

基础知识(看不懂)

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;
}
View Code

 


PART3(算法的延伸应用)

 

PART4(对算法深度的理解)

 DFS在思考时要从最底层想起(不会再继续递归下去的那一层)

PART5(与其相关的有趣题目)

 

posted @ 2021-07-05 19:49  bear_xin  阅读(24)  评论(0编辑  收藏  举报