推荐一下神仙的博客
t a r j a n tarjan tarjan拖到这个时候才来学真的是我菜.
这次一定要简单了说.
/*
tarjan 可以用来求强连通分量.
可以把同一个强连通分量中的点全都标上同一个数字.
我们接下来就看一看怎么写的.
*/
首先我们思考一下,对整张图进行深度优先搜索,用
d
f
n
dfn
dfn数组标记每个点是第几个搜索到的.
那么有概率出现一个非常奇妙的事情:
一个点dfs过去的时候忽地指向了它自己!
这时候从这个点
d
f
s
dfs
dfs刚才的一圈当然就是个强连通子图了.
然而重要的是,这个不能算是强连通分量啊,要极大的才算.
那怎么办啊?
我们骚一个数组
l
o
w
low
low,代表所有和某个点构成强连通分量的点中最小的
d
f
n
dfn
dfn.
这样只要一个点没有一个儿子(即比它后面
d
f
s
dfs
dfs到的点)指向比它前面
d
f
s
dfs
dfs到的点(当然这些点都得在同一个强连通分量里),这个点的
d
f
n
dfn
dfn就等于
l
o
w
low
low.那么这个问题就解决了.
现在还剩下怎么判断一个节点连向的节点是和它在同一个强连通分量里的.
开栈记录.
好了,游戏结束.
好玩的开始了
首先你需要准备面粉,鸡蛋,烤箱,奶油
d
f
n
,
l
o
w
,
v
i
s
,
s
t
a
c
k
(
栈
)
dfn,low,vis,stack(栈)
dfn,low,vis,stack(栈)这4个数组(栈).
接下来你需要燃起来.
fuko dfn,low,vis,col;
stack<int> lxy;//栈随便取一个名字
int cnt,ans;
void tarjan(int u){
dfn[u]=low[u]=++cnt;//首先给u分配一个编号
vis[u]=1,lxy.push(u);//标明u在栈里并加入栈.
for (int v:lj[u]){
if (!dfn[v]||vis[v]){//如果v没有编号(没有dfs过)或者已经在此强连通子图里.
if (!dfn[v]) tarjan(v);//如果v没有编号,去跑它.
low[u]=min(low[u],low[v]);//更新low.
}
}
if (dfn[u]==low[u]){//它自己是编号最小的点.
col[u]=++ans,vis[u]=0;//染色,退栈
for (;lxy.top()^u;lxy.pop()){//这个强连通分量都退栈
col[lxy.top()]=ans;
vis[lxy.top()]=0;
}lxy.pop();//最后把u退栈.
}
}
那么强连通分量就搞定了.
例题什么的都在上面给的博客里.
缩点
求了强连通分量,就可以把每个强连通分量缩成一个非常大的点,点权可以自定. 这样原来的图转化为一个DAG,做题从未如此轻松.
我来写一下上面的博客没有给出的洛谷缩点模板(当时那个博客写的时候没有这个模板).
/*
首先缩成的非常大的点的那个权值非常容易知道,就是包含的所有点的权值之和.
我们把原来的边保存一下,缩完点之后把不在同一个强连通分量中的边重新连上.
(卧槽开了80000的边,直接re)
然后开一个DAGdp,每个点的答案就是它的权值加上所有它指向的边的最大权值.
(反正DAG,直接爆搜就可以了.)
然后就过去了.
*/
#include<bits/stdc++.h> //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rec register char
#define rel register ll
#define gc getchar
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
inline int read(){
int x=0,f=1;char c=gc();
for (;!isdigit(c);c=gc()) f^=c=='-';
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return f?x:-x;
}
template <typename mitsuha>
inline bool read(mitsuha &x){
x=0;int f=1;char c=gc();
for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
if (!~c) return 0;
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return x=f?x:-x,1;
}
template <typename mitsuha>
inline int write(mitsuha x){
if (!x) return 0&pc(48);
if (x<0) x=-x,pc('-');
int bit[20],i,p=0;
for (;x;x/=10) bit[++p]=x%10;
for (i=p;i;--i) pc(bit[i]+48);
return 0;
}
inline char fuhao(){
char c=gc();
for (;isspace(c);c=gc());
return c;
}
}using namespace chtholly;
using namespace std;
typedef pair<int,int> edge;
const int yuzu=1e5;
typedef int fuko[yuzu|10];
vector<int> lj[yuzu|10];
fuko dfn,low,vis,col,du,dp,a,r;
stack<int> lxy;
int cnt,ans,n,m;
edge e[yuzu<<3|10];
void tarjan(int u){
dfn[u]=low[u]=++cnt;
vis[u]=1,lxy.push(u);
for (int v:lj[u]){
if (!dfn[v]||vis[v]){
if (!dfn[v]) tarjan(v);
low[u]=min(low[u],low[v]);
}
}
if (dfn[u]==low[u]){
col[u]=++ans,vis[u]=0;
for (;lxy.top()^u;lxy.pop()){
vis[lxy.top()]=0;
col[lxy.top()]=ans;
r[ans]+=a[lxy.top()];
}
r[ans]+=a[u],lxy.pop();
}
}
void dfs(int u){
if (dp[u]) return;
dp[u]=r[u];
int zxy=0;
for (int i:lj[u]){
if (!dp[i]) dfs(i);
zxy=max(zxy,dp[i]);
}dp[u]+=zxy;
}
int main(){
int i,j;n=read(),m=read();
for (i=1;i<=n;++i) a[i]=read();
for (i=1;i<=m;++i){
int u=read(),v=read();
lj[u].push_back(v);
e[i]=edge(u,v);
}
for (i=1;i<=n;++i){
if (!dfn[i]) tarjan(i);
}
for (i=1;i<=n;++i) lj[i].clear();
for (i=1;i<=m;++i){
int u=e[i].first,v=e[i].second;
if (col[v]^col[u]) lj[col[u]].push_back(col[v]);
}
int llx=0;
for (i=1;i<=ans;++i){
if (!dp[i]) dfs(i),llx=max(llx,dp[i]);
}write(llx);
}
求割点和割边.
这里就只讲求割点了.
如果在无向图中去掉某个点u使得该图不连通了,则u就是该图的一个割点.
很明显了.我画个图来看一看.

标红星的就是个割点.显然割点之后的点连边不可能连到比该割点
d
f
n
dfn
dfn要小的点.
据此修改
t
a
r
j
a
n
tarjan
tarjan的代码如下.
fuko dfn,low,vis,is_cut;
int cnt,ans;
int tarjan(int u,int fa){
int ch=0,lowu;
dfn[u]=lowu=++cnt;
for (int v:lj[u]){
if (!dfn[v]){
++ch;
int lowv=tarjan(v,u);
lowu=min(lowu,lowv);
if (lowv>dfn[u]) is_cut[u]=1;
}
else{
if (v^fa&&dfn[v]<dfn[u]){
lowu=min(lowu,dfn[v]);
}
}
}
if (!fa&&ch==1) is_cut[u]=0;//注意如果根节点有两个或者以上的儿子,根节点是割点.
return low[u]=lowu;
}
然后瞎搞过模板.
好了.谢谢大家.


9769

被折叠的 条评论
为什么被折叠?



