在一个有向图中,有一个起点R,对于任意点W,对于R->W的任意路径都经过点P,则称P为W的支配点。设idom[i]表示距离i最近的支配点。在原图基础上,idom[i]向i连边构成一颗新树,称为支配树
1.支配树是以R为根的一棵树
2.对于任意点i,到根r路径上经过的点集{xi}是原图上r->i的必经点
3.对于任意的i,它是子树中每个点的必经点
在dfs搜索树中,对于一个节点Y,存在某个点X能够通过一系列点pi(不包含X和Y)到达点Y且?i dfn[i]>dfn[Y],我们就称X是Y的半必经点,记做semi[Y]=X
通俗理解:semi[x]就是x在dfs树中所有祖先中z,能不经过 z? 和 x? 之间的树上的点而到达 x? 的点中深度最小的。
证明的话,咕咕了
保留dfs树的树边,连接semi[i]->i构建新图。不改变原图支配关系,且构成一个dag图。
对于点x,有边(y,x)
? 若dfn[y]$<$dfn[x](树边或前向边) 且dfn[y]$<$dfn[semi[x]] ,semi[x]=y
? 根据定义显然
? 若dfn[y]>dfn[x](后向边或横叉边),找到y的一个祖先semi值最小
的z且dfn[z]>dfn[x],用semi[z]更新semi[x]
HDU4694
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
vector<int>fg[N];vector<int>gg[N];vector<int>ng[N];
int n,m,cnt,tot,rt;
int num[N],val[N],size[N],du1[N],du2[N],fa[N],dfn[N],semi[N],idom[N],fir[N],nxt[N],ver[N],col[N];
void init(){
cnt=tot=rt=0;
for(int i=1;i<=n;i++)fg[i].clear(),gg[i].clear(),ng[i].clear();
memset(val,0,sizeof val);memset(num,0,sizeof num);
memset(du1,0,sizeof du1);memset(du2,0,sizeof du2);
memset(fa,0,sizeof fa);memset(dfn,0,sizeof dfn);
memset(semi,0,sizeof semi);memset(idom,0,sizeof idom);
memset(fir,0,sizeof fir);memset(nxt,0,sizeof nxt);
memset(ver,0,sizeof ver);memset(col,0,sizeof col);
}
inline void add(int x,int y){ver[++tot]=y,nxt[tot]=fir[x];fir[x]=tot;}
void dfs(int x){
dfn[x]=++cnt,num[cnt]=x;
for(int i=fir[x];i;i=nxt[i]){
int y=ver[i];if(dfn[y]) continue;
fa[y]=x;dfs(y);
}
}
int find(int x){
if(col[x]==x) return x;
int fx=find(col[x]);
if(dfn[semi[val[col[x]]]]<dfn[semi[val[x]]]) val[x]=val[col[x]];
col[x]=fx;return fx;
}
void tarjan(){
for(int j=cnt;j>1;j--){
int x=num[j];
for(int i=0;i<fg[x].size();i++){
int y=fg[x][i];if(!dfn[y]) continue;
find(y);
if(dfn[semi[val[y]]]<dfn[semi[x]])
semi[x]=semi[val[y]];
}
gg[semi[x]].push_back(x);
col[x]=fa[x];int now=fa[x];
for(int i=0;i<gg[now].size();i++){
int x=gg[now][i];
find(x);
if(semi[val[x]]==now) idom[x]=now;
else idom[x]=val[x];
}
}
for(int i=2;i<=cnt;i++){
int x=num[i];
if(idom[x]!=semi[x]) idom[x]=idom[idom[x]];
ng[idom[x]].push_back(x);
}
}
void dfsans(int x,int sum){
du2[x]=x+sum;
for(int i=0;i<ng[x].size();i++)
dfsans(ng[x][i],sum+x);
}
int main(){
//freopen("nmmp.cpp","r",stdin);
while(scanf("%d%d",&n,&m)!=EOF){
int x,y;init();
for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),add(x,y),fg[y].push_back(x);
for(int i=1;i<=n;i++){col[i]=val[i]=semi[i]=i;}
dfs(n);tarjan();dfsans(n,0);
for(int i=1;i<n;i++) printf("%d ",du2[i]);
printf("%d",du2[n]);puts("");
}
return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#pragma GCC optimize(3)
using namespace std;
const int MAXN = 50000 + 10;
int n, m;
int read()
{
int x = 0, f = 1;
static char c = getchar();
while ( c<'0' || c>'9' )
{
if ( c == '-' )
f = -1;
c = getchar();
}
while ( c >= '0' && c <= '9' )
{
x = (x << 1) + (x << 3) + (c ^ '0'); c = getchar();
}
return(x * f);
}
struct Graph {
vector<int> G[MAXN];
void add( int f, int t )
{
G[f].push_back( t );
}
void reset()
{
for ( int i = 1; i <= n; i++ )
G[i].clear();
}
} G, rG, sG, tree;
int dfn[MAXN], id[MAXN], dfn_cnt, semi[MAXN], idom[MAXN], dep[MAXN];
int degree[MAXN];
int fa[MAXN];
int lca[MAXN][20];
int belong[MAXN], mn[MAXN]; /* bcj */
bool vis[MAXN];
int q[MAXN], top;
int sum;
queue<int> Q;
int ans[MAXN];
void reset()
{
dfn_cnt = 0; top = 0; sum = 0;
memset( lca, 0, sizeof(lca) );
memset( vis, false, sizeof(vis) );
memset( fa, 0, sizeof(fa) );
memset( degree, 0, sizeof(degree) );
memset( dfn, 0, sizeof(dfn) );
memset( id, 0, sizeof(id) );
memset( semi, 0, sizeof(semi) );
memset( dep, 0, sizeof(dep) );
memset( ans, 0, sizeof(ans) );
memset( mn, 0, sizeof(mn) );
for ( int i = 1; i <= n; i++ )
{
belong[i] = i; mn[i] = i;
}
G.reset(), sG.reset(), tree.reset(), rG.reset();
}
void getdfn( int x, int f )
{
dfn[x] = ++dfn_cnt; id[dfn_cnt] = x;
semi[x] = x; fa[x] = f;
vis[x] = true;
for ( int i = 0; i < G.G[x].size(); i++ )
{
int t = G.G[x][i];
if ( vis[t] )
continue;
getdfn( t, x );
}
}
int find( int x )
{
if ( x == belong[x] )
return(x);
int f = belong[x]; belong[x] = find( f );
if ( dfn[semi[mn[f]]] < dfn[semi[mn[x]]] )
mn[x] = f;
return(fa[x]);
}
int LCA( int x, int y )
{
if ( dep[x] < dep[y] )
swap( x, y );
int d = dep[x] - dep[y];
for ( int i = 18; i >= 0; i-- )
if ( d & (1 << i) )
x = lca[x][i];
for ( int i = 18; i >= 0; i-- )
{
if ( lca[x][i] ^ lca[y][i] )
x = lca[x][i], y = lca[y][i];
}
return(x == y ? x : lca[x][0]);
}
void worklca( int x )
{
int anc = sG.G[x][0];
for ( int i = 0; i < sG.G[x].size(); i++ )
{
anc = LCA( anc, sG.G[x][i] );
}
dep[x] = dep[anc] + 1;
idom[x] = anc;
lca[x][0] = anc;
for ( int i = 1; i <= 18; i++ )
lca[x][i] = lca[lca[x][i - 1]][i - 1];
}
void lengauer_tarjan()
{
getdfn( n, 0 );
dfn[0] = 0x3f3f3f3f;
for ( int w = dfn_cnt; w > 1; w-- )
{
int x = id[w];
for ( int i = 0; i < rG.G[x].size(); i++ )
{
int t = rG.G[x][i];
if ( !dfn[t] )
continue;
find( t );
if ( dfn[semi[mn[t]]] < dfn[semi[x]] )
semi[x] = semi[mn[t]];
}
sG.add( semi[x], x );
belong[x] = fa[x];
int now = fa[x];
for ( int i = 0; i < sG.G[now].size(); i++ )
{
int x = sG.G[now][i];
find( x );
if ( semi[mn[x]] == now )
idom[x] = now;
else idom[x] = mn[x];
}
}
for ( int i = 2; i <= dfn_cnt; i++ )
{
int x = id[i];
if ( idom[x] != semi[x] )
idom[x] = idom[idom[x]];
tree.add( x, idom[x] );
}
}
int getans( int x )
{
int ans = x;
for ( int i = 0; i < tree.G[x].size(); i++ )
{
ans += getans( tree.G[x][i] );
}
return(ans);
}
int main()
{
while ( scanf( "%d%d", &n, &m ) != EOF )
{
int f, t;
reset();
for ( int i = 1; i <= m; i++ )
{
f = read(), t = read();
G.add( f, t ); rG.add( t, f );
}
lengauer_tarjan();
for ( int i = 1; i <= n; i++ )
printf( "%d ", getans( i ) );
puts( "" );
}
}
原文:https://www.cnblogs.com/BeyondStars/p/12380664.html