强联通分量——学习笔记
2017-08-02 22:26
183 查看
温馨提示:接下来讨论的情况全部建立在有向图的基础上。
如果两个点i,j满足i能到j且j能到i,则i和j强联通。所有点都强连通的图是强连通图,一个有向图中的所有最大(就是不能再扩大了)的强连通子图都是这个有向图的强连通分量。强联通分量貌似除了环缩点也没别的作用了。求有向图中的强联通分量可以用Tarjan算法,用递归的方法。复杂度为O(n);
Tarjan的核心就是两个数组dfn和low,其中dfn[i]是i的访问时刻,用一个time(官方说法叫时间戳,并不明白是啥么玩意儿),而low[i]表示i能追溯到的最早时刻。
接着再开一个栈stk,表示目前处理的强连通分量中的点。访问一个点i时,先进行初始化dfn[i]=low[i]v=++ti,然后枚举i的儿子节点们,假设目前处理的i的儿子是son,那么分为两种情况
1.sin还没访问过,然后先访问,然后就追溯到low[son],所以这时low[i]=min(low[i],low[son]);
2.son已访问,此时再分两种
(1) son不在栈里,就表示和i没什么关系了.
(2)son还在栈里,这时用子树节点更新节点第一次出现的时间low[i]=min(low[i],dfn[son]);
给个题 BZOJ 1179 抢掠计划:
经典的tarjan环缩点假spfa刷最长路。
如果两个点i,j满足i能到j且j能到i,则i和j强联通。所有点都强连通的图是强连通图,一个有向图中的所有最大(就是不能再扩大了)的强连通子图都是这个有向图的强连通分量。强联通分量貌似除了环缩点也没别的作用了。求有向图中的强联通分量可以用Tarjan算法,用递归的方法。复杂度为O(n);
Tarjan的核心就是两个数组dfn和low,其中dfn[i]是i的访问时刻,用一个time(官方说法叫时间戳,并不明白是啥么玩意儿),而low[i]表示i能追溯到的最早时刻。
接着再开一个栈stk,表示目前处理的强连通分量中的点。访问一个点i时,先进行初始化dfn[i]=low[i]v=++ti,然后枚举i的儿子节点们,假设目前处理的i的儿子是son,那么分为两种情况
1.sin还没访问过,然后先访问,然后就追溯到low[son],所以这时low[i]=min(low[i],low[son]);
2.son已访问,此时再分两种
(1) son不在栈里,就表示和i没什么关系了.
(2)son还在栈里,这时用子树节点更新节点第一次出现的时间low[i]=min(low[i],dfn[son]);
给个题 BZOJ 1179 抢掠计划:
经典的tarjan环缩点假spfa刷最长路。
#include<cstdio> #include<cstring> #include<algorithm> #define maxn 500005 using namespace std; int n,e,m,s,tot[2],tem,sum,top,a[maxn],b[maxn],fa[maxn],lnk[2][maxn],son[2][maxn],nxt[2][maxn],low[maxn],dfn[maxn],stk[maxn],dst[maxn],que[maxn]; bool instk[maxn],vs[maxn]; inline void readi(int &x){ x=0; char ch=getchar(); while ('0'>ch||ch>'9') ch=getchar(); while ('0'<=ch&&ch<='9') {x=x*10+ch-'0'; ch=getchar();} } void _add(int t,int x,int y){ tot[t]++; son[t][tot[t]]=y; nxt[t][tot[t]]=lnk[t][x]; lnk[t][x]=tot[t]; } void _dfs(int x){ dfn[x]=low[x]=++tem; instk[x]=true; stk[++top]=x; for (int j=lnk[0][x];j;j=nxt[0][j]){ if (!dfn[son[0][j]]) {_dfs(son[0][j]); if (low[x]>low[son[0][j]]) low[x]=low[son[0][j]];} else if (instk[son[0][j]]&&low[x]>dfn[son[0][j]]) low[x]=dfn[son[0][j]]; } if (dfn[x]!=low[x]) return; int y; sum++; do{ y=stk[top--]; fa[y]=sum; instk[y]=false; b[sum]+=a[y]; }while (x!=y); } void _spfa(){ memset(vs,0,sizeof(vs)); memset(dst,192,sizeof(dst)); int hed=0,til=1; que[1]=fa[s]; vs[fa[s]]=true; dst[fa[s]]=b[fa[s]]; while (hed!=til){ hed=(hed+1)%maxn; vs[que[hed]]=false; for (int j=lnk[1][que[hed]];j;j=nxt[1][j]){ if (dst[son[1][j]]<dst[que[hed]]+b[son[1][j]]){ dst[son[1][j]]=dst[que[hed]]+b[son[1][j]]; if (!vs[son[1][j]]){ til=(til+1)%maxn; que[til]=son[1][j]; vs[son[1][j]]=true; if (dst[son[1][j]]>dst[que[(hed+1)%maxn]]) swap(que[til],que[(hed+1)%maxn]); } } } } } int main() { readi(n); readi(e); tot[0]=tot[1]=0; memset(lnk,0,sizeof(lnk)); memset(nxt,0,sizeof(nxt)); for (int i=1;i<=e;i++){ int x,y; readi(x); readi(y); _add(0,x,y); } for (int i=1;i<=n;i++) readi(a[i]); tem=sum=top=0; memset(b,0,sizeof(b)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(stk,0,sizeof(stk)); memset(instk,0,sizeof(instk)); for (int i=1;i<=n;i++) if (!dfn[i]) _dfs(i); // for (int i=1;i<=n;i++) printf("%d ",fa[i]); printf("\n"); for (int i=1;i<=n;i++) for (int j=lnk[0][i];j;j=nxt[0][j]) if (fa[i]!=fa[son[0][j]]) _add(1,fa[i],fa[son[0][j]]); readi(s); _spfa(); readi(m); int ans=0; // for (int i=1;i<=sum;i++) printf("%d ",dst[i]); printf("\n"); for (int i=1;i<=m;i++){ int p; readi(p); if (ans<dst[fa[p]]) ans=dst[fa[p]]; } printf("%d",ans); return 0; }
相关文章推荐
- tanjan算法求强联通分量
- Tarjan强联通分量【模板】
- Vijos P1023Victoria的舞会3【贪心+DFS求强联通分量】
- poj 1904 King's Quest 二分图中强联通分量的运用
- 【图论】强联通分量
- 强联通分量 缩点 tarjan 入门题小集
- UVALive3523 [Knights of the Round Table] tarjan求无向图双联通分量
- 边双联通分量小结+模板
- 割点,割边,强联通分量,点双联通分量,边双联通分量
- CodeForces 209C Trails and Glades(欧拉回路判断+并查集计算联通分量)
- Connections Gym - 101630C DFS/有向图的强联通分量边集
- tarjan 强联通分量和割点
- POJ 3352 Road Construction&& POJ 3177 Redundant Paths 双联通分量
- 无向图的边双联通分量,点双联通分量
- 【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点
- [HDOJ4738]Caocao's Bridges(双联通分量,割边,tarjan)
- 强联通分量tarjan算法(模板)
- Tarjian算法求强联通分量【STL_stack的运用】
- HDU 3861 强联通分量+最小路径覆盖
- POJ 3592 Instantaneous Transference(强联通分量 Tarjan)