[BZOJ1093][ZJOI2007]最大半连通子图 强联通+拓扑排序+dp 做题笔记

题目来源:http://www.lydsy.com/JudgeOnline/problem.php?id=1093

Tarjan求scc,在缩点后的图跑拓排求最长链。在拓排树进行dp。拓排针对层级问题进行,先处理完了一个节点的前驱在处理该节点,除去了后效性,故可以在拓排树上dp。
注意SCC缩点后可能有重边需特判。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
using namespace std;
int t=0,tot=1,n,m,mx=0,scc=0,ans=0,top=0,MOD;
const int N=100005,M=1000005;
int head[N],e[M<<1],ver[M<<1],next[M<<1];
int dfn[N],low[N],q[N<<1],bel[N],hav[N];
int ind[N],vis[N],f[N],g[N];
vector<int> G[N];
bool inq[N];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

void add (int u,int v) {
    ver[++tot]=v;next[tot]=head[u];head[u]=tot;
}

void Tarjan (int x) {
    dfn[x]=low[x]=++t;
    q[++top]=x; inq[x]=1;
    for (int i=head[x];i;i=next[i]) 
        if (!dfn[ver[i]])
            Tarjan(ver[i]),low[x]=min(low[x],low[ver[i]]);
        else if (inq[ver[i]]) low[x]=min(low[x],dfn[ver[i]]);
    int now=0;
    if (dfn[x]==low[x]) {
        scc++;
        while (now!=x) {
            now=q[top--];
            inq[now]=0;
            hav[scc]++;
            bel[now]=scc;
        }
    }
}

void rebuild () {
    for (int x=1;x<=n;x++) 
        for (int i=head[x];i;i=next[i])
            if (bel[x]!=bel[ver[i]])
                G[bel[x]].push_back(bel[ver[i]]),++ind[bel[ver[i]]];//
}

void dp () {
    queue<int> q;
    for (int i=1;i<=scc;i++) {
        if (!ind[i]) q.push(i);
        f[i]=hav[i]; g[i]=1;
    }
    while (!q.empty()) {
        int now=q.front();int v; q.pop();
        for (int i=0;i<G[now].size();i++) {
            ind[v=G[now][i]]--;
            if (!ind[v]) q.push(v);
            if (vis[v]==now) continue;//新图可能有重边。
            if (f[now]+hav[v]>f[v]) {
                f[v]=f[now]+hav[v];
                g[v]=g[now];
            }
            else if (f[now]+hav[v]==f[v])
                g[v]=(g[v]+g[now])%MOD;
            vis[v]=now;
        }
    }   
}

int main () {
    int u,v;
    n=read(); m=read(); MOD=read();
    for (int i=1;i<=m;i++) {
        u=read(); v=read();
        add(u,v);
    }
    for (int i=1;i<=n;i++) if (!dfn[i]) Tarjan(i);
    rebuild();
    dp();
    for (int i=1;i<=scc;i++) {
        if (f[i]>mx)mx=f[i],ans=g[i];
        else if (f[i]==mx) ans=(ans+g[i])%MOD;
    }
    printf("%d\n%d\n",mx,ans);
    return 0;
}
    原文作者:拓扑排序
    原文地址: https://blog.csdn.net/mhlwsk/article/details/50831553
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞