学习笔记 支配树
注明参考出处
膜拜巨佬之余
来一个
最优是
(这里默认nm数量级同阶)
1.定义
对于一棵可以存在环的有向图
给定一个起点
那么我们就称之为
我们令支配
那么从起点
支配树可以对于每一个点
这个点我们称之为
如果每一个点连出指向
2.算法
1.首先我们需要一颗
也就是对这个算法进行
2.半必经点
从
若
若
从半支配点到支配点
对于
寻找方法如下
我们令
如果
算法流程
我们用带权并查集来实现
首先按照
而并查集的权 就是并查集这个点到根节点的路径上的所有点 最小的
找
CODE:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
#include<queue>
#include<map>
#include<stack>
#include<list>
#include<set>
#include<deque>
#include<vector>
#include<ctime>
#define ll long long
#define inf 0x7fffffff
#define N 500008
#define IL inline
#define M 1008611
#define D double
#define ull unsigned long long
#define R register
using namespace std;
template<typename T>void read(T &a)
{
T x=0,f=1;char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')f=0;ch=getchar();
}
while(isdigit(ch))
{
x=(x<<1)+(x<<3)+ch-'0';ch=getchar();
}
a=f?x:-x;
}
/*-------------OI使我快乐-------------*/
int n,m,tot,cnt;
int head[M<<1],pre[N<<1],to[N<<2],nex[N<<2],lat[M],cdy[M];
int bel[M],val[M],sdom[M],idom[M],ans[M];
int dfn[M],id[M],fa[M];
IL void add(int *h,int x,int y)
{to[++tot]=y;nex[tot]=h[x];h[x]=tot;}
IL void dfs(int now)
{
dfn[now]=++cnt;id[cnt]=now;
for(R int i=head[now];i;i=nex[i])
{
int v=to[i];
// printf("%d --> %d\n",now,v);
if(dfn[v]) continue;
dfs(v);fa[v]=now;
}
}
IL int find(int x)
{
if(x==bel[x]) return x;
int root=find(bel[x]);
if(dfn[sdom[val[bel[x]]]]<dfn[sdom[val[x]]])
val[x]=val[bel[x]];
return bel[x]=root;
}
IL void Tarjan()
{
// for(R int i=1;i<=n;++i) printf("dfn[%d] %d\n",i,dfn[i]);
for(R int i=cnt;i>=2;--i)
{
int now=id[i];
//我们按照dfs序 从大到小处理 节点
for(R int j=pre[now];j;j=nex[j])
{//这里用链式前向星 存入度节点
int v=to[j];
if(!dfn[v]) continue;
find(v);
//dfn[y]<dfn[x]的话
//我们还未处理到 sdom[y]=val[y]=y
//此时y的并查集树中只有TA自己 更新是合法的
//dfn[y]>dfn[x]的话
//就按照套路比较
if(dfn[sdom[val[v]]]<dfn[sdom[now]])
sdom[now]=sdom[val[v]];
}
add(lat,sdom[now],now);
bel[now]=fa[now];
//同其在dfs树上的父亲连边
now=fa[now];
//现在父亲到TA这棵子树已经处理完了
//所以此时我们可以对
//以父亲为sdom[x]的x分别求一次sdom 然后清空
//对于每一个点进行更新
for(R int j=lat[now];j;j=nex[j])
{
int v=to[j];
find(v);
if(sdom[val[v]]==now) idom[v]=now;
//如果sdom[z]==sdom[x] 那么idom[x]=sdom[x]
//此时sdom[x]=now
else idom[v]=val[v];
//否则就是idom[x]=idom[z]
//但是实际实现我们可以写成idom[x]=z
//然后在接下来处理
}
}
for(R int i=2,now;i<=cnt;++i)
{
now=id[i];
if(idom[now]!=sdom[now]) idom[now]=idom[idom[now]];
//这是残余节点的填坑大作战
}
}
IL void dfs_ans(int now)
{
ans[now]=1;
for(R int i=cdy[now];i;i=nex[i])
{
int v=to[i];
dfs_ans(v);
ans[now]+=ans[v];
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
read(n);read(m);
for(R int i=1,x,y;i<=m;++i)
{
read(x);read(y);
add(head,x,y);add(pre,y,x);
}
for(R int i=1;i<=n;++i)
sdom[i]=bel[i]=val[i]=i;
dfs(1);
Tarjan();
// puts("How old are you ? ");
// for(int i=1;i<=n;++i)
// {
// printf("支配点[%d] %d\n",i,idom[i]);
// }
tot=0;
for(R int i=2;i<=n;++i) if(idom[i]) add(cdy,idom[i],i);
dfs_ans(1);
for(R int i=1;i<=n;++i) printf("%d%c",ans[i],(i==n ? '\n':' '));
// fclose(stdin);
// fclose(stdout);
return 0;
}