Tarjan&&缩点简析

由于昨天写计蒜客初赛的一道题,看出了是缩点,但一时忘记了另外一个叫什么s...的算法怎么写了,话说我为什么没有回去翻一下自己的blog然后今天就去学了更实用也更强力的Tarjan

Tarjan的思想其实很简单,就是用时间戳(讲得真TM流弊,其实就是DFS访问到的次序)和栈来搞一下

关于Tarjan的写法,我自己也不是很讲得来,大家可以看一下这篇blog

这里讲一下如何用Tarjan来进行缩点

令我们求出的col[i]表示原图中第i个点是哪个强连通分量中的,则我们可以建出新图,方式如下

for (i=1;i<=n;++i)
for (j=head[i];j!=-1;j=e[j].next)
if (col[i]!=col[e[j].to]) add(col[i],col[e[j].to]);

很玄学?但是它就是正确的,然后我们就得到了缩过点的新图

像有这里有一道模板题:Luogu P3387 【模板】缩点

题意很简单,我们先Tarjan缩点,缩完的点的点权就是它这个强连通分量中的所有点的点权和

然后就是一个DAG(有向无环图)了,我们DP,记搜,拓扑排序都可以

这里我还是习惯性的写了BFS的topo

CODE

#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e4+5,M=1e5+5;
struct edge
{
    int to,next;
}e[M],ne[M];
int n,m,head[N],nhead[N],a[N],v[N],dfn[N],low[N],stack[N],col[N],dis[N],q[N],ru[N],top,cnt,x,y,tot,sum;
bool vis[N];
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void add(int x,int y)
{
    e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt;
}
inline void nadd(int x,int y)
{
    ne[++cnt].to=y; ne[cnt].next=nhead[x]; nhead[x]=cnt;
}
inline int min(int a,int b)
{
    return a<b?a:b;
}
inline int max(int a,int b)
{
    return a>b?a:b;
}
inline void Tarjan(int now)
{
    dfn[now]=low[now]=++tot;
    stack[++top]=now; vis[now]=1;
    for (register int i=head[now];i!=-1;i=e[i].next)
    if (!dfn[e[i].to])
    {
        Tarjan(e[i].to);
        low[now]=min(low[now],low[e[i].to]);
    } else
    {
        if (vis[e[i].to]) low[now]=min(low[now],dfn[e[i].to]);
    }
    if (dfn[now]==low[now])
    {
        col[now]=++sum; 
        v[sum]+=a[now]; vis[now]=0;
        while (now!=stack[top])
        {
            col[stack[top]]=sum;
            v[sum]+=a[stack[top]]; vis[stack[top--]]=0;
        } --top;
    }
}
inline int topo(void)
{
    memset(dis,0,sizeof(dis));
    register int i; int ans=0;
    int H=0,T=0;
    for (i=1;i<=sum;++i)
    if (!ru[i]) q[++T]=i,dis[i]=v[i],ans=max(dis[i],ans);
    while (H<T)
    {
        int now=q[++H];
        for (i=nhead[now];i!=-1;i=ne[i].next)
        {
            dis[ne[i].to]=max(dis[ne[i].to],dis[now]+v[ne[i].to]);
            ans=max(ans,dis[ne[i].to]);
            if (!(--ru[ne[i].to])) q[++T]=ne[i].to;
        }
    }
    return ans;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i,j;
    memset(head,-1,sizeof(head));
    memset(e,-1,sizeof(e));
    read(n); read(m);
    for (i=1;i<=n;++i)
    read(a[i]);
    for (i=1;i<=m;++i)
    read(x),read(y),add(x,y);
    for (i=1;i<=n;++i)
    if (!dfn[i]) Tarjan(i);
    memset(nhead,-1,sizeof(head));
    memset(ne,-1,sizeof(e)); cnt=0;
    for (i=1;i<=n;++i)
    for (j=head[i];j!=-1;j=e[j].next)
    if (col[i]!=col[e[j].to]) nadd(col[i],col[e[j].to]),++ru[col[e[j].to]];
    printf("%d",topo());
    return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/9032472.html