• [dsu on tree]【学习笔记】


    十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 [Codeforces原文链接](http://codeforces.com/blog/entry/44351#comment-332425)

    dsu on tree

    简介

    我也不清楚dsu是什么的英文缩写...
    好吧是Disjoint Set Union 并查集2333
    就像是树上的启发式合并
    用到了(heavy-light decomposition)树链剖分
    把轻边子树的信息合并到重链上的点里
    因为每次都是先dfs轻儿子再dfs重儿子,只有重儿子子树的贡献保留,所以可以保证dfs到每颗子树时当前全局维护的信息不会有别的子树里的,和莫队很像


    算法过程

    find the BigChild of each vertex
    dfs(u, fa, keep)
        dfs(LightChild, u, 0)
        dfs(BigChild, u, 1), big[BigChild] = 1
        update(u, fa, 1) //calculate the contribution of u's LightChild's SubTree
        update the ans of u
        big[BigChild] = 0
        if keep == 0 
            update(u, fa, -1) //remove the contributino of u's SubTree
            
    update(u, fa, val)
        calculate u's information
        update(v : (u, v) and !big[v], u, val)
        
    

    先递归计算轻儿子子树,递归结束时消除他们的贡献
    再递归计算重儿子子树,保留他的贡献
    再计算当前子树中所有轻子树的贡献
    更新答案
    如果当前子树是父节点的轻子树,消除当前子树的贡献


    复杂度分析

    显然只有遇到轻边才会把自己的信息合并到父节点
    树链剖分后每个点到根的路径上有(logn)条轻边和(lgon)条重链
    一个点的信息只会向上合并(logn)
    如果一个点的信息修改是(O(1))的,那么总复杂度就是(O(nlogn))
    从这里我们可以发现和对dfs序使用莫队有异曲同工之妙,莫队也要求修改的复杂度很低


    应用

    1. 优秀的dfs序莫队替代品,复杂度(sqrt{n} ightarrow logn)
    2. 结合点分治的思想可以做一些有根树上的路径统计问题

    模板题

    CF600E. Lomsat gelral
    题意:询问每颗子树中出现次数最多的颜色们编号和

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    #define pii pair<int, ll>
    #define MP make_pair 
    #define fir first
    #define sec second
    const int N=1e5+5;
    int read(){
        char c=getchar();int x=0,f=1;
        while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
        while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
        return x*f;
    }
    
    int n, a[N];
    struct edge{int v, ne;}e[N<<1];
    int cnt, h[N];
    inline void ins(int u, int v) {
    	e[++cnt]=(edge){v, h[u]}; h[u]=cnt;
    	e[++cnt]=(edge){u, h[v]}; h[v]=cnt;
    }
    int size[N], mx[N], big[N];
    void dfs(int u, int fa) {
    	size[u]=1;
    	for(int i=h[u];i;i=e[i].ne) 
    		if(e[i].v != fa) {
    			dfs(e[i].v, u);
    			size[u] += size[e[i].v];
    			if(size[e[i].v] > size[mx[u]]) mx[u] = e[i].v;
    		}
    }
    
    int cou[N], Max; ll ans[N];
    pii f[N];
    void update(int u, int fa, int val) {
    	int &c = cou[a[u]];
    	f[c].fir --; f[c].sec -= a[u];
    	c += val;
    	f[c].fir ++; f[c].sec += a[u];
    	if(val==1) Max = max(Max, c);
    	else if(!f[Max].fir) Max--;
    
    	for(int i=h[u];i;i=e[i].ne) 
    		if(e[i].v != fa && !big[e[i].v]) update(e[i].v, u, val);
    }
    
    void dfs(int u, int fa, int keep) {
    	for(int i=h[u];i;i=e[i].ne) 
    		if(e[i].v != fa && e[i].v != mx[u]) dfs(e[i].v, u, 0);
    	if(mx[u]) dfs(mx[u], u, 1), big[mx[u]] = 1;
    	update(u, fa, 1);
    	ans[u] = f[Max].sec;
    	big[mx[u]] = 0;
    	if(!keep) update(u, fa, -1);
    }
    int main() {
    	//freopen("in","r",stdin);
    	n=read();
    	for(int i=1; i<=n; i++) a[i]=read();
    	for(int i=1; i<n; i++) ins(read(), read());
    	dfs(1, 0);
    	dfs(1, 0, 1);
    	for(int i=1; i<=n; i++) printf("%I64d ",ans[i]);
    }
    
    

  • 相关阅读:
    获取Android状态栏高度的屡试不爽的方法
    在线音乐API的研究 (Part 2.1)
    Zabbix
    利用 Puppet 实现自动化管理配置 Linux 计算机集群
    django的admin后台管理如何更改为中文
    windows系统安装python3.6.3和python3.7.0
    微课程--Android--高级控件之二--标题栏
    微课程--Android--高级控件之一ListView
    微课程--Android--Fragement
    微课程--Android--界面布局总结
  • 原文地址:https://www.cnblogs.com/candy99/p/dsuontree.html
Copyright © 2020-2023  润新知