zoukankan      html  css  js  c++  java
  • Dsu on Tree总结

    Dsu on Tree(树上启发式合并)

    关于Dsu?

    并查集(亦称Ufs)。

    然而本算法和并查集并没有半毛钱关系。

    有什么用?

    可以在(O(nlogn))的时间复杂度内解决大部分不带修改的子树信息查询问题。

    算法过程?

    一个dfs,可能还需要写一个辅助函数Add。

    简述一下算法过程:

    假设现在dfs到了(x)结点。

    1. dfs进入轻子树,离开这棵轻子树后从统计数组中暴力清除这棵轻子树中的信息(用另一个dfs(即Add,参数(kk=-1))递归遍历整棵轻子树并从统计数组中清除每个结点的信息)。

    2. dfs进入重子树,结束后不清除这棵重子树的信息(可以认为(x)继承了其重儿子(heavychild[x])的信息)。

    3. 用另一个dfs(即Add,参数(kk=+1))暴力统计x的所有轻子树的信息并计入统计数组。

    4. 此时统计数组内的信息即为以(x)为根的子树的信息,此时可以回答所有关于子树(x)的询问。

    这个算法好暴力!

    辣鸡博主别欺负我读书少,这不是(O(n^2))的吗?

    别急,我们来冷静分析一下这个算法的时间复杂度。

    显然,如果只考虑dfs1和的dfs2(不考虑其中的Add),这个算法显然是(O(n))的。

    现在我们来看看一个结点(x)会被哪些结点出发的Add递归访问到。

    显然如果结点(x)会被从结点(y)出发的Add递归访问到,(y)必须是(x)到根路径上的结点。

    分析算法过程,我们可以发现,Add的出发节点必须作为其父结点的一个轻儿子,即只可能是其所在重链的顶端结点。

    考虑到任何一个结点到根结点的路径上最多只有(logn)条重链,也就是最多有(logn)个合法的Add的出发结点,因此每个结点最多被Add访问到(O(logn))次。自然,算法总时间复杂度(O(nlogn))得证。

    程序实现?

    dfs1求出每个结点的重儿子

    这里的实现方法和树剖完全相同。

    void dfs1(int x,int depth){
    	dep[x]=depth;
    	siz[x]=1;
    	int maxsiz=-1;
    	trav(i,x){
    		int ver=e[i].to;
    		dfs1(ver,depth+1);
    		siz[x]+=siz[ver];
    		if(siz[ver]>maxsiz){
    			maxsiz=siz[ver];
    			pc[x]=ver;
    		}
    	}
    }
    
    dfs2统计答案
    void Add(int x,int kk){
    	if(kk==1){
    		统计这个结点的贡献;
    	}
    	else if(kk==-1){
    		清除这个结点的贡献;
    		(这里可以直接清空深度为dep[x]的统计数组)
    	}
    	trav(i,x){
    		int ver=e[i].to;
    		if(mark[ver]) continue;
    		Add(ver,kk);
    	}
    }
    
    void dfs2(int x,bool Keep){
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==pc[x]) continue;
    		dfs2(ver,0);
    	}//1
    	if(pc[x]) dfs2(pc[x],1);//2
    	mark[pc[x]]=1;
    	Add(x,1);//3(这里通过标记重儿子可防止Add进入x的重子树,相当于以下注释代码)
    //	trav(i,x){
    //		int ver=e[i].to;
    //		if(ver==pc[x]) continue;
    //		Add(ver,1);
    //	}
    	mark[pc[x]]=0;
    	rin(i,0,(int)vec[x].size()-1)
    		ans[vec[x][i].id]=...;//4(用std::vector储存结点x上的询问
    	if(!Keep) Add(x,-1);//这里不能memset,否则会TLE
    }
    

    一些其它的写法(摘自Codeforces

    vector<int> *vec[maxn];
    int cnt[maxn];
    void dfs(int v, int p, bool keep){
    	int mx = -1, bigChild = -1;
    	for(auto u : g[v])
    	   if(u != p && sz[u] > mx)
    		   mx = sz[u], bigChild = u;
    	for(auto u : g[v])
    	   if(u != p && u != bigChild)
    		   dfs(u, v, 0);
    	if(bigChild != -1)
    		dfs(bigChild, v, 1), vec[v] = vec[bigChild];
    	else
    		vec[v] = new vector<int> ();
    	vec[v]->push_back(v);
    	cnt[ col[v] ]++;
    	for(auto u : g[v])
    	   if(u != p && u != bigChild)
    		   for(auto x : *vec[u]){
    			   cnt[ col[x] ]++;
    			   vec[v] -> push_back(x);
    		   }
    	//now (*cnt[v])[c] is the number of vertices in subtree of vertex v that has color c. You can answer the queries easily.
    	// note that in this step *vec[v] contains all of the subtree of vertex v.
    	if(keep == 0)
    		for(auto u : *vec[v])
    			cnt[ col[u] ]--;
    }
    
    int cnt[maxn];
    void dfs(int v, int p, bool keep){
    	int mx = -1, bigChild = -1;
    	for(auto u : g[v])
    	   if(u != p && sz[u] > mx)
    		  mx = sz[u], bigChild = u;
    	for(auto u : g[v])
    		if(u != p && u != bigChild)
    			dfs(u, v, 0);  // run a dfs on small childs and clear them from cnt
    	if(bigChild != -1)
    		dfs(bigChild, v, 1);  // bigChild marked as big and not cleared from cnt
    	for(auto u : g[v])
    	if(u != p && u != bigChild)
    		for(int p = st[u]; p < ft[u]; p++)
    		cnt[ col[ ver[p] ] ]++;
    	cnt[ col[v] ]++;
    	//now cnt[c] is the number of vertices in subtree of vertex v that has color c. You can answer the queries easily.
    	if(keep == 0)
    		for(int p = st[v]; p < ft[v]; p++)
    		cnt[ col[ ver[p] ] ]--;
    }
    

    例题?

    CF570D Tree Requests

    代码
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <algorithm>
    #include <vector>
    #define rin(i,a,b) for(int i=(a);i<=(b);i++)
    #define rec(i,a,b) for(int i=(a);i>=(b);i--)
    #define trav(i,a) for(int i=head[(a)];i;i=e[i].nxt)
    using std::cin;
    using std::cout;
    using std::endl;
    typedef long long LL;
    
    inline int read(){
    	int x=0;char ch=getchar();
    	while(ch<'0'||ch>'9') ch=getchar();
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x;
    }
    
    const int MAXN=500005;
    int n,m;
    int ecnt,head[MAXN];
    int dep[MAXN],siz[MAXN],pc[MAXN];
    int cnt[MAXN][30],cnt1[MAXN];
    bool mark[MAXN],ans[MAXN];
    char ch[MAXN];
    struct Edge{
    	int to,nxt;
    }e[MAXN<<1];
    struct Quest{
    	int d,id;
    };
    std::vector<Quest> vec[MAXN];
    
    inline void add_edge(int bg,int ed){
    	ecnt++;
    	e[ecnt].to=ed;
    	e[ecnt].nxt=head[bg];
    	head[bg]=ecnt;
    }
    
    void dfs1(int x,int depth){
    	dep[x]=depth;
    	siz[x]=1;
    	int maxsiz=-1;
    	trav(i,x){
    		int ver=e[i].to;
    		dfs1(ver,depth+1);
    		siz[x]+=siz[ver];
    		if(siz[ver]>maxsiz){
    			maxsiz=siz[ver];
    			pc[x]=ver;
    		}
    	}
    }
    
    void Add(int x,int kk){
    //	if(kk==1){
    		cnt[dep[x]][ch[x]-'A']+=kk; 
    		if(cnt[dep[x]][ch[x]-'A']&1) cnt1[dep[x]]++;
    		else cnt1[dep[x]]--;
    //	}
    //	else{
    //		cnt[dep[x]][ch[x]-'A']=0;
    //		cnt1[dep[x]]=0;
    //	}
    //这里去掉注释也对 
    	trav(i,x){
    		int ver=e[i].to;
    		if(mark[ver]) continue;
    		Add(ver,kk);
    	}
    }
    
    void dfs2(int x,bool Keep){
    	if(pc[x]) dfs2(pc[x],1);
    	mark[pc[x]]=1;
    	Add(x,1);
    	mark[pc[x]]=0;
    	trav(i,x){
    		int ver=e[i].to;
    		if(ver==pc[x]) continue;
    		dfs2(ver,0);
    	}
    	rin(i,0,(int)vec[x].size()-1)
    		ans[vec[x][i].id]=(cnt1[vec[x][i].d]<2);
    //	if(!Keep){
    //		memset(cnt,0,sizeof cnt);
    //		memset(cnt1,0,sizeof cnt1);
    //	}
    //这里去掉注释并把下面加上注释也对,但是会T 
    	if(!Keep) Add(x,-1);
    }
    
    int main(){
    	n=read(),m=read();
    	rin(i,2,n){
    		 int u=read();
    		 add_edge(u,i);
    	}
    	rin(i,1,n){
    		char temp=getchar();
    		while(!isalpha(temp)) temp=getchar();
    		ch[i]=temp;
    	}
    	rin(i,1,m){
    		int x=read(),d=read();
    		vec[x].push_back((Quest){d,i});
    	}
    	dfs1(1,1);
    	dfs2(1,1);
    	rin(i,1,m){
    		if(ans[i]) printf("Yes
    ");
    		else printf("No
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    An intriguing failing of convolutional neural networks and the CoordConv solution
    Arbitrary Style Transfer in Real-time with Adaptive Instance Normalization
    Win10下用Anaconda安装TensorFlow
    批处理框架
    智能分单算法
    海量数据处理利器greenplum——初识
    使用R画地图数据
    跟我一起ggplot2(1)
    一文搞懂HMM(隐马尔可夫模型)
    借助亚马逊S3和RapidMiner将机器学习应用到文本挖掘
  • 原文地址:https://www.cnblogs.com/ErkkiErkko/p/10003622.html
Copyright © 2011-2022 走看看