luogu_3387【题解】缩点 tarjan

题目:https://www.luogu.org/problemnew/show/P3387

如题目描述,缩点加DP

怎么缩点????

tarjan算法,主要在于维护了一个low[ ] 数组。

low 数组表示一个点可以到达的最浅的点的dfs序。

如果一个点的low[ ]数组与自己的dfs序相等,证明自己整个子树是强连通分量。

强连通分量就可以缩成一个点。

tarjan代码如下(来源:洛谷网校课件)

int s[MAXN], stop;
int dfn[MAXN], low[MAXN];
int scccnt, sccnum[MAXN];
int dfscnt;

inline void tarjan(int now){
    dfn[now] = low[now] = ++dfscnt;
    s[stop++] = now;
    for (int i = he[now]; i != 0 ; i = ne[i]){
        if (!dfn[ed[i]]) {
            tarjan(ed[i]);
            low[now] = min(low[now], low[ed[i]]);
        } else if(!sccnum[ed[i]]) {
            low[now] = min(low[now], dfn[ed[i]]);
        }
    }

    if (dfn[now] == low[now]) {
        scccnt++;
        do {
            sccnum[s[--stop]] = scccnt;
        } while(s[stop] != now);
    }
}

这个代码中,sccnum[ ]存的就是缩后的点的顺序。(重新排列了。)

下个代码则是,sccnum[ ]存的缩之前强连通分量的最浅的点的编号,也就是now。

代码如下。

int s[maxn], stop;
int dfn[maxn],low[maxn],dfscnt,sccnum[maxn];
inline void tarjan(int now){
    dfn[now]=low[now]=++dfscnt;
    s[stop++]=now;
    for(int i=head[now];i;i=nxt[i]){
        int t=to[i];
        if(!dfn[t]){
            tarjan(t);
            low[now]=min(low[now],low[t]);
        }else if(!sccnum[t]){
            low[now]=min(low[now],dfn[t]);
        }
    }
    if(dfn[now]==low[now]){
        do{
            int t=s[--stop];
            sccnum[t]=now;   //主要区别。
            if(now!=t)
                d[now]+=d[t];
        }while(s[stop]!=now);
    }
}

那么这道题也就可以解决了。

DP则简单。

就是重新在缩点之后连边,从子节点中更新。

代码如下。

#include<bits/stdc++.h>
#define sc(x) scanf("%d",&x)
using namespace std;
const int maxn=1e4+10,maxm=1e5+10;
int n,m,tot;
int d[maxn],head[maxm],nxt[maxm<<1],to[maxm<<1],from[maxm<<1];
inline void add(int u,int v){
    to[++tot]=v,from[tot]=u;
    nxt[tot]=head[u],head[u]=tot;
}
int s[maxn], stop;
int dfn[maxn],low[maxn],dfscnt,sccnum[maxn];
inline void tarjan(int now){
    dfn[now]=low[now]=++dfscnt;
    s[stop++]=now;
    for(int i=head[now];i;i=nxt[i]){
        int t=to[i];
        if(!dfn[t]){
            tarjan(t);
            low[now]=min(low[now],low[t]);
        }else if(!sccnum[t]){
            low[now]=min(low[now],dfn[t]);
        }
    }
    if(dfn[now]==low[now]){
        do{
            int t=s[--stop];
            sccnum[t]=now;   
            if(now!=t)
                d[now]+=d[t];
        }while(s[stop]!=now);
    }
}
struct node{
    int from,to,nxt;
}e[maxm<<1];
int he[maxm],cnt,du[maxn],ans,dis[maxn];
inline void topo()
{
    queue<int> q;
    for(int i=1;i<=n;i++)
        if(sccnum[i]==i&&!du[i]){
            q.push(i);dis[i]=d[i];
        }
    while(!q.empty()){
        int k=q.front();q.pop();
        for(int i=he[k];i;i=e[i].nxt){
            int t=e[i].to;
            dis[t]=max(dis[t],dis[k]+d[t]);
            if(!--du[t]) q.push(t);
        }
    }
    for(int i=1;i<=n;i++)
        ans=max(ans,dis[i]);
}
int main()
{
    sc(n),sc(m);
    for(int i=1;i<=n;i++)
        sc(d[i]);
    for(int i=1;i<=m;i++){
        int u,v;
        sc(u),sc(v);
        add(u,v);
    }
    for(int i=1;i<=n;i++) 
        if(!dfn[i]) tarjan(i);
    for(int i=1;i<=m;i++){
        int u=sccnum[from[i]],v=sccnum[to[i]];
        if(u!=v){
            e[++cnt].from=u;
            e[cnt].to=v;
            e[cnt].nxt=he[u];
            he[u]=cnt;
            du[v]++;
        }
    }
    topo();
    printf("%d\n",ans);
    system("pause");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ChrisKKK/p/10976542.html