• dsu on tree详解


    dsu 中文名是并查集,然而除了启发式合并,这个算法和并查集并没啥关系。

    适用范围:

    1.没有修改

    2.询问子树

    流程

    模板题

    题解

    暴力显然(O(n^2)) O(TLE)

    先分析一下暴力的思路,我们是对每一棵子树做O(n)的统计

    更确切地说,我们是 对一个节点的儿子的那棵子树做统计,清空,下一个儿子,清空,下下一个儿子,清空......该节点的树做统计,清空

    我们发现,在 对该节点的树做统计 前,最后一个儿子的那棵子树统计不用清空

    并且,这个不用清空的儿子size越大,显然越优

    然后,这就是dsu on tree了

    时间复杂度O(nlogn)

    核心代码

    void dfs2(int x,int fa,int opt)
    {
    	for(int i=head[x];i;i=nt[i])
    		if(to[i]!=fa&&to[i]!=son[x])dfs2(to[i],x,0);
    	if(son[x])dfs2(son[x],x,1),Son=son[x];
    	ADD(x,fa,1);Son=0;ans[x]=nowans;
    	if(!opt)ADD(x,fa,-1),nowans=0,mx=0;
    }
    

    时间复杂度

    显然,一个节点到根的所有边中,轻边不会超过log n条

    考虑每个点的贡献

    1、通过轻边被访问到。根据前面的性质,被访问次数 < log n
    2、通过重边被访问到。显然只有一次

    总体 O(nlog n)

    总结

    先想出一个关于每个节点的子树的暴力

    然后套上dsu on tree,把复杂度的一个n变成log n

    练习题

    还有好多水题,自己做吧

    CF600E 题解
    CF570D 题解
    CF208E听说各种方法都能过,主流dsu或差分
    CF246E同上
    CF1009F裸题
    CF375D
    wannafly Day2 E dsu+set
    牛客练习赛60E
    ccpc2020长春站F题
    洛谷P4149
    牛客练习赛81D
    CF741D

    P6623 [省选联考 2020 A 卷] 树

    2020省选D2T2现场的时候能用这算法水60分,具体思路就是对二进制下每一位分别处理,处理的时候用dsu on tree+树状数组

    upd:貌似能拿100分,可能是我的常数太大了。。。

    代码如下

    #include<iostream>
    #include<cstdio>
    #define LL long long
    using namespace std;
    int n,tot;
    const int N=530010;
    int v[N],fa[N],head[N],to[N],nt[N];
    void add(int f,int t)
    {
    	to[++tot]=t;nt[tot]=head[f];head[f]=tot;
    }
    namespace solve1
    {
    	LL ans;
    	int val[N];
    	void dfs(int x,int dep,int FA)
    	{
    		val[FA]^=(v[x]+dep);
    		for(int i=head[x];i;i=nt[i])dfs(to[i],dep+1,FA);
    	}
    	void work()
    	{
    		for(int i=1;i<=n;++i)dfs(i,0,i),ans+=val[i];
    		cout<<ans;
    	}
    }
    namespace solve2
    {
    	LL ans;int sum,Son,now,bg;
    	int siz[N],son[N];
    	struct SZSZ
    	{
    		int tr[N<<2],ling;
    		int lowbit(int x){return x&(-x);}
    		void add(int pos,int v)
    		{
    			if(pos==0){ling+=v;return;}
    			for(;pos<=bg;pos+=lowbit(pos))tr[pos]+=v;
    		}
    		int ask(int pos)
    		{
    			if(pos<0)return 0;
    			int res=ling;
    			for(;pos;pos-=lowbit(pos))res+=tr[pos];
    			return res;
    		}
    		int wen(int l,int r)
    		{
    			if(l<=r)return ask(r)-ask(l-1);
    			else return ask(r)+ask(bg)-ask(l-1);
    		}
    	}A;
    	void dfs0(int x,int dep)
    	{
    		v[x]+=dep;siz[x]=1;
    		for(int i=head[x];i;i=nt[i])
    		{
    			dfs0(to[i],dep+1);
    			siz[x]+=siz[to[i]];
    			if(siz[to[i]]>siz[son[x]])son[x]=to[i];
    		}
    	}
    	void suan(int x,int opt)
    	{
    		A.add(v[x]&bg,opt);
    		for(int i=head[x];i;i=nt[i])
    			if(to[i]!=Son)suan(to[i],opt);
    	}
    	void dfs(int x,int dep,int opt)
    	{
    		
    		for(int i=head[x];i;i=nt[i])
    			if(to[i]!=son[x])dfs(to[i],dep+1,0);
    		if(son[x])dfs(son[x],dep+1,1),Son=son[x];
    		suan(x,1);
    		if(A.wen(((1<<(now-1))+dep)&bg,(bg+dep)&bg)&1)++sum;
    		if(!opt)Son=0,suan(x,-1);
    	}
    	void work()
    	{
    		dfs0(1,1);
    		int mx=0;
    		for(int i=1;i<=n;++i)mx=max(mx,v[i]);
    		for(int i=1;(1<<(i-1))<=mx+1;++i)
    		{
    			sum=0;now=i;bg=(1<<i)-1;
    			dfs(1,1,0);
    			ans+=(LL)sum*(1<<(i-1));
    		}
    		cout<<ans;
    	}
    }
    int main()
    {
    	cin>>n;
    	for(int i=1;i<=n;++i)scanf("%d",&v[i]);
    	for(int i=2;i<=n;++i)scanf("%d",&fa[i]),add(fa[i],i);
    	if(n<=3000)solve1::work();
    	else solve2::work();
    	return 0;
    }
    
  • 相关阅读:
    MyBatis简介
    JDBC报错:Cannot find class: com.mysql.jdbc.Driver
    进程,线程,协程
    mamp提示 more elements...
    获取html页面的参数
    php基础函数
    返回接口
    生成订单号
    xml转数组
    判断是否是微信浏览器
  • 原文地址:https://www.cnblogs.com/wljss/p/14962016.html
Copyright © 2020-2023  润新知