题目链接

P3387 【模板】缩点

解题思路

这几天搞图论,好有趣hhh,多写几篇博客。

上次学\(Tarjan\)求割点,这次缩点。

思路大概是多一个栈和染色的步骤,每次\(Tarjan\)的时候把点入栈,如果某个点(比较像割点但不完全是)的\(DFS\)子树都搜不到它祖宗,那么接下来进行的遍历操作必然与该点不能形成强连通分量,所以可以遇到\(low[p]==dfn[p]\)的点就把栈里面的东西全弹出来,染色表示这是一个强连通分量。

对于这个题,强连通分量之间再进行连边,记忆化搜索(类似树形DP)即可。

AC代码

#include<stdio.h>
#include<string.h>
#define N 100010
#define M 10010
#define min(a,b) (a>b?b:a)
#define max(a,b) (a<b?b:a)
int n,m;
int v[M],x[N],y[N];

struct Edge{
    int end,near;
}e[N];
int head[M],cnt;
void add(int a,int b){
    e[++cnt].end=b;
    e[cnt].near=head[a];
    head[a]=cnt;
}

int dfn[M],tot,sta[M],top,vis[M],low[M];
int color[M],num,val[M];
void tarjan(int p){
    dfn[p]=low[p]=++tot;
    sta[++top]=p;vis[p]=1;
    int i;
    for(i=head[p];i;i=e[i].near){
        int q=e[i].end;
        if(!dfn[q])tarjan(q),low[p]=min(low[p],low[q]);
        else if(vis[q])low[p]=min(low[p],low[q]);
    }
    if(dfn[p]==low[p]){
        num++;
        while(sta[top+1]!=p){
            color[sta[top]]=num;
            vis[sta[top]]=0;
            val[num]+=v[sta[top]];
            top--;
        }
    }
}

int f[M];
void dp(int p){
    int i;
    for(i=head[p];i;i=e[i].near){
        int q=e[i].end;
        if(!f[q])dp(q);
        f[p]=max(f[p],f[q]);
    }
    f[p]+=val[p];
}

int main(){
    int i,ans=0;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&v[i]);
    for(i=0;i<m;i++)scanf("%d%d",&x[i],&y[i]),add(x[i],y[i]);
    for(i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);//tarjan缩点 
    memset(head,0,sizeof(head));cnt=0;//清空边 
    for(i=0;i<m;i++)//强连通分量之间连边 
        if(color[x[i]]^color[y[i]])add(color[x[i]],color[y[i]]);
    for(i=1;i<=num;i++)if(!f[i])dp(i),ans=max(ans,f[i]);//DP
    printf("%d",ans);
    return 0;
}