zoukankan      html  css  js  c++  java
  • 【学习小记】支配树【图论】

    Preface

    给定一个有向图和一个起点(st),我们需要知道起点到某个点的关于必经点的信息。
    若起点到点v的所有路径均经过点u,则我们称点u支配点v,显然一个点支配自己本身

    顾名思义,支配树就是由某些支配关系构成的树。

    定义

    约定一些记号
    ((u,v)),表示一条从u到v的有向边
    (fa(u)),表示(u)在DFS树上的父亲。
    (dfn(i))为从(st)开始DFS整个图,(i)号点的(dfs)时间戳。
    (Path(u,v)),表示DFS树上(u)(v)的路径上的点集
    (Path(u,v)/u)表示路径上点集中除去点(u)剩余的集合

    我们定义一个点(u)的最近支配点(idom(u)),它支配(u),且它也被除u以外的所有支配u的点支配

    性质


    显然(idom(u))唯一存在,且若我们重新建一个图,所有的(idom(u))(u)连一条边,那么这个新图是一棵以(st)为根的树,也就是说(idom)不会成环(可以用反证法简单证明)。
    这棵树就是我们说的支配树。


    (idom(u))一定是(u)(dfs)树上的祖先。(一样可以反证法简单证明)


    问题就是要求出(idom)

    求解

    为了求出(idom),我们引入半必经点的概念。

    半必经点

    回归tarjan求强连通分量的算法,我们来观察DFS过程中会出现哪些边。

    • 树边,DFS树上的父亲——儿子边。
    • 后向边,祖先向后代连的非树边的边。
    • 返祖边,DFS序大的后代向DFS序小的祖先连的边
    • 横叉边,DFS序大的点向DFS序小的点连的边,且没有祖先后代关系。

    容易证明不存在上面四种边以外的边。

    (semi(u))表示从(u)开始,沿着边反着走,能够中途不经过u的祖先,最后到达的深度最浅((dfn)最小)的(u)的祖先。

    形式化的,(semi(u))(u)的祖先,且存在原图中的一条路径(p_1...p_k)(p_1=semi(u),p_k=u,forall_{i=2}^{k-1}dfn(p_i)>dfn(u))

    可以发现这两种定义是等价的,因为不可能通过反向边走到DFS序更小的且不是祖先的点去。

    我们考虑它可能怎么走,要么通过树边/后向边的反向边直接走到一个祖先,要么通过返祖边/横叉边的反向边走到某些(dfn)更大的点(pre_u),然后在(Path(st,pre_u))上找一个(w),满足(dfn(w)geq dfn(u)),所有的(semi(w))的最浅的那个就用来更新(semi(u))

    可以发现最浅的(semi(w))一定是(u)的祖先,因此我们不必担心不合法的问题。

    那么我们就得到了(semi)的求法,按照DFS序倒序枚举所有点(u),按照上面的做法来不断更新,查询祖先可以用带权并查集来做,求出一个点的(semi)之后将它的所有树边儿子与它合并即可。

    问题在于求(idom)
    我们考虑(vin Path(semi(u),u)/semi(u)),找到(semi(v))最浅的一个。

    有定理:若(semi(v)=semi(u)),则(idom(u)=semi(u)),否则(idom(u)=idom(v))
    这个定理请自行理解...我也不会证

    有了这个定理之后我们可以将(u)挂在(semi(u))上,扫到(semi(u))的时候就找到(u)相应的(v),若(semi(u)=semi(v))就直接(idom(u)=semi(u)),否则因为此时(idom(v))还没有求出来,我们可以先将(idom(u))指向(v),最后全部都做完以后按照DFS序正序扫一遍将这部分的(idom(u))改成(idom(idom(u)))即可。

    时间复杂度(O(nlog n))(因为不能按秩合并)

    Code

    洛谷P5180,支配树模板题。

    #include <bits/stdc++.h>
    #define fo(i,a,b) for(int i=a;i<=b;++i)
    #define fod(i,a,b) for(int i=a;i>=b;--i)
    #define N 200005
    #define M 300005
    using namespace std;
    vector<int> pre[N];
    int fs[N],nt[M],dt[M],pr[M],n,m,m1;
    
    int semi[N],idom[N],ft[N],fp[N],dfn[N],dfw[N],fa[N];
    vector<int> ids[N];
    int gmin(int x,int y) 
    {
    	return (!y||(dfn[x]<dfn[y]&&x))?x:y;
    }
    void link(int x,int y)
    {
    	nt[++m1]=fs[x];
    	dt[fs[x]=m1]=y;
    }
    void dfs(int k)
    {
    	dfw[dfn[k]=++dfn[0]]=k;
    	for(int i=fs[k];i;i=nt[i])
    	{
    		int p=dt[i];
    		if(!dfn[p]) fa[p]=k,dfs(p);
    		if(dfn[k]<dfn[p]) semi[p]=gmin(semi[p],k);
    		else pre[p].push_back(k);
    	}
    }
    int getf(int k)
    {
    	if(!ft[k]||ft[k]==k) return k;
    	int p=getf(ft[k]);
    	fp[k]=(dfn[semi[fp[k]]]<dfn[semi[fp[ft[k]]]])?fp[k]:fp[ft[k]];
    	return ft[k]=p;
    }
    int fd(int k)
    {
    	getf(k);return fp[k];
    }
    void merge(int x,int y)
    {
    	int fx=getf(x),fy=getf(y);
    	ft[fy]=fx,fp[fy]=(dfn[semi[fp[fy]]]<dfn[semi[fp[fx]]])?fp[fy]:fp[fx];
    }
    void make()
    {
    	fod(i,n,1)
    	{
    		int k=dfw[i];
    		if(i>1)
    		{
    			for(int u:pre[k])
    			{	
    				int v=fd(u);
    				semi[k]=gmin(semi[k],semi[v]);
    			}
    			ids[semi[k]].push_back(k);	
    		}
    		for(int u:ids[k])
    		{
    			int v=fd(u);
    			idom[u]=(semi[v]==k)?k:v; 
    		}
    		fp[k]=k;
    		for(int i=fs[k];i;i=nt[i]) if(fa[dt[i]]==k) merge(k,dt[i]);
    	}
    	for(int i=2,k=dfw[2];i<=n;++i,k=dfw[i]) if(idom[k]!=semi[k]) idom[k]=idom[idom[k]];
    }
    int sz[N];
    int main()
    {
    	cin>>n>>m;
    	fo(i,1,m)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		link(x,y),pre[y].push_back(x);
    	}
    	dfs(1);
    	make();
    	fod(i,n,1)
    	{
    		sz[dfw[i]]++;
    		sz[idom[dfw[i]]]+=sz[dfw[i]];	
    	}
    	fo(i,1,n) printf("%d ",sz[i]);
    }
    
  • 相关阅读:
    机器学习:逻辑回归(基础理解)
    机器学习:模型泛化(L1、L2 和弹性网络)
    机器学习:模型泛化(LASSO 回归)
    机器学习:模型泛化(岭回归:Ridge Regression)
    Numpy:np.random 的使用
    ASP.NET MVC 视图层-生成链接相关(Html.ActionLink,Url.Action)
    eval实例
    <a>实现按钮的javascript+jquery编程实例
    SQL Server之XML PATH()
    分页数算法
  • 原文地址:https://www.cnblogs.com/BAJimH/p/11090030.html
Copyright © 2011-2022 走看看