zoukankan      html  css  js  c++  java
  • 【学习笔记】Dsu On Tree

    推荐!!cf上的教程

    Dsu On Tree 是什么

    针对树上问题的一种“优雅的暴力”,类似树上启发式合并。
    基于轻重链剖分。

    使用条件:

    1. 离线,无修改。
    2. 与子树有关,要遍历所有子树。

    复杂度:(O(nlogn))


    解决问题示例

    Problem

    (CF600E)
    一棵 (n) 个节点的树,每个结点有一个颜色。
    问,以每个节点为根的子树中,出现次数最多的颜色是哪种(若多种,输出种类编号之和)。

    朴素的暴力

    以每个点为根,遍历每棵子树统计答案。
    复杂度 (O(n^2))

    优雅的暴力

    在朴素的暴力中,许多重复的信息没有重复利用。
    比如,当遍历完一棵子树后,我们将它对答案的贡献清除了。但其实在遍历其父节点的子树时,这棵子树的信息是有用的。

    在递归 (dfs) 中,可不可以将某些子树的答案保留,再将其它子树的答案暴力并上去,得到父节点的答案?
    当然 (ok)
    而那棵被保留的子树,贪心地想应是节点数最多的子树,即“重子”。

    于是基本思路就出来了——
    先做一遍轻重链剖分。然后在递归时先递归轻子,之后将答案清除;再递归重子,将答案保留;随后将轻点的答案暴力加上,统计本节点的答案。
    这就是 (dsu) (on) (tree) !撒花!

    复杂度证明

    轻重链剖分有个性质:每个点到根的路径上最多只有 (logn) 条轻边。
    证明:每走过一条轻边,子树大小一定小于之前的一半。把 (n) 减半,最多减 (logn) 次。

    本算法中,只有轻边所连的子树会被暴力统计。
    考虑每个点被暴力统计多少次,即其在多少个轻边所连子树中,其实就是它到根节点的轻边个数 ,即 (logn)
    故,总复杂度 (nlogn)

    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    
    int read(){
    	int x=0;
    	char ch=getchar();
    	while(!isdigit(ch)) ch=getchar();
    	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
    	return x;
    }
    
    const int N = 100005;
    typedef long long ll;
    
    struct node{
    	int v;
    	node *nxt;
    }pool[N*2],*h[N];
    int cnt;
    void addedge(int u,int v){
    	node *p=&pool[++cnt],*q=&pool[++cnt];
    	p->v=v;p->nxt=h[u];h[u]=p;
    	q->v=u;q->nxt=h[v];h[v]=q;
    }
    
    int sz[N],son[N],fa[N];
    void dfs1(int u){
    	int v,Mson=0;
    	sz[u]=1;
    	for(node *p=h[u];p;p=p->nxt)
    		if(!fa[v=p->v]){
    			fa[v]=u;
    			dfs1(v);
    			sz[u]+=sz[v];
    			if(sz[v]>Mson) Mson=sz[v],son[u]=v;
    		}
    }
    
    int n,c[N],t[N],mx;
    ll ans[N],num;
    
    void Plus(int x,int y) { t[x]+=y; if(t[x]==mx) num+=x; if(t[x]>mx) mx=t[x],num=x; }
    void add(int u,int x){
    	int v;
    	Plus(c[u],x);
    	for(node *p=h[u];p;p=p->nxt)
    		if(fa[v=p->v]==u) add(v,x);
    }
    void dfs(int u,int ty){
    	int v;
    	for(node *p=h[u];p;p=p->nxt)
    		if(fa[v=p->v]==u && v!=son[u]) dfs(v,0);
    	if(son[u]) dfs(son[u],1);
    	for(node *p=h[u];p;p=p->nxt) 
    		if(fa[v=p->v]==u && v!=son[u]) add(v,1);
    	Plus(c[u],1);
    	ans[u]=num;
    	if(ty==0) add(u,-1),mx=0,num=0;
    }
    
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++) c[i]=read();
    	for(int i=1;i<n;i++) addedge(read(),read());
    	
    	fa[1]=-1; dfs1(1);
    	dfs(1,1);
    	
    	for(int i=1;i<=n;i++) printf("%lld ",ans[i]);
    	
    	return 0;
    }
    

    算法(伪)代码

    //轻重链剖分
    void dfs1(int u){
    	int v,Mson=0;
    	sz[u]=1;
    	for(node *p=h[u];p;p=p->nxt)
    		if(!fa[v=p->v]){
    			fa[v]=u;
    			dfs1(v);
    			sz[u]+=sz[v];
    			if(sz[v]>Mson) Mson=sz[v],son[u]=v;
    		}
    }
    //dsu on tree
    void Plus(int u,int x);
    void add(int u,int x){ //暴力枚举轻子子树求贡献
            Plus(u,x);
            for(node *p=h[u];p;p=p->nxt) add(p->v,x);
    }
    void cal();
    void work(int u,int ty){
            int v;
            for(node *p=h[u];p;p=p->nxt)
                if((v=p->v)!=son[u]) work(v,0);//递归处理轻子,处理后清理
            if(son[u]) work(son[u],1);//递归处理重子,之后不清理
            
            for(node *p=h[u];p;p=p->nxt)
                if((v=p->v)!=son[u]) add(v,1); //暴力加上轻子的答案
            Plus(u,1);//加上本节点的答案
            
            cal(); //统计
    
            if(ty==0) {  //若本节点为轻点,则需清除贡献
                add(u,-1); 
                与答案有关变量=0;//能全清为零,是由于轻点在重点之前递归,所有贡献均为该轻点内的贡献,而无需保留的重点的贡献。
            }
    } 
    

    其他例题及应用

    (详见 (cf) 博客)
    (cf570D) 也是个模板题
    还可以解决一些点分治的问题。

    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    谷歌浏览器中安装JsonView扩展程序
    谷歌浏览器中安装Axure扩展程序
    PreferencesUtils【SharedPreferences操作工具类】
    Eclipse打包出错——提示GC overhead limit exceeded
    IntentActionUtil【Intent的常见作用的工具类】
    DeviceUuidFactory【获取设备唯一标识码的UUID(加密)】【需要运行时权限的处理的配合】
    AndroidStudio意外崩溃,电脑重启,导致重启打开Androidstudio后所有的import都出错
    DateTimeHelper【日期类型与字符串互转以及日期对比相关操作】
    ACache【轻量级的开源缓存框架】
    WebUtils【MD5加密(基于MessageDigest)】
  • 原文地址:https://www.cnblogs.com/lindalee/p/11815217.html
Copyright © 2011-2022 走看看