NC20603 [ZJOI2007]最大半连通子图

题目:

• 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:u,v∈V,满足u→v或v→u,即对于图中任意 两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。
• 若G'=(V',E')满足V'∈V,E'是E中所有跟V'有关的边, 则称G'是G的一个导出子图。
• 若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。
• 若G'是G所有半连通子图 中包含节点数最多的,则称G'是G的最大半连通子图。
• 给定一个有向图G,请求出G的最大半连通子图拥有的节点数K ,以及不同的最大半连通子图
的数目C。由于C可能比较大,仅要求输出C对X的余数。

题解:

• 如果两点互相可以到达,那么它们必是半连通图,所以考虑先Tarjan缩点,接着去除重边重新建图,你会发现,在这个有向无环图(DAG)中,半连通子图都是一条链(可以举反例试试,这条链不可能有分支,否则将有两点无法抵达另一方)
• 于是,G的最大半连通子图拥有的节点数K就是最长链长度,不同的最大半连通子图的数目就是最长链个数。
• 最长链可以直接用拓扑排序(topo),最长链个数用一个类似DP的方法,用f【i】表示以 i为终点的方案数,那么f【i】就等于满足距离为起点到 i 的临时最短距离的点的 f 的和。然后查找距离等于最长链的点,答案为它们的方案数之和

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10,maxm=1e6+10;
struct edge
{
    int u,v,nxt;
}e[maxm],ee[maxm];
int head[maxn],js,headd[maxn],jss;
void addage(edge e[],int head[],int &js,int u,int v)
{
    e[++js].u=u;e[js].v=v;
    e[js].nxt=head[u];head[u]=js;
}
int dfn[maxn],low[maxn],cnt,st[maxn],top,lt[maxn],lts,ltn[maxn];
void  tarjan(int u)
{
    dfn[u]=low[u]=++cnt;
    st[++top]=u;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!lt[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u])
    {
        lt[u]=++lts;ltn[lts]++;
        while(st[top]!=u)lt[st[top--]]=lts,ltn[lts]++;
        --top;
    }
}
int n,m,x;
int f[maxn],ff[maxn];
int cd[maxn],rd[maxn];
int maxd,maxf;
int pc[maxn];
queue<int>q;
void dfs()
{
    while(!q.empty())
    {
        int u=q.front();q.pop();
        maxd=max(maxd,f[u]);
        for(int i=headd[u];i;i=ee[i].nxt)
        {
            int v=ee[i].v;
            rd[v]--;
            if(rd[v]==0)q.push(v);
            if(pc[v]==u)continue;
            if(f[u]+ltn[v]>f[v])
            {
                f[v]=f[u]+ltn[v];
                ff[v]=ff[u];

            }
            else if(f[u]+ltn[v]==f[v])
            {
                ff[v]=(ff[u]+ff[v])%x;
            }
            pc[v]=u;
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&x);
    for(int u,v,i=1;i<=m;++i)
    {
        scanf("%d%d",&u,&v);
        addage(e,head,js,u,v);
    }
    for(int i=1;i<=n;++i)
        if(!dfn[i])tarjan(i);
    for(int u=1;u<=n;++u)
        for(int i=head[u];i;i=e[i].nxt)
            if(lt[e[i].u]!=lt[e[i].v])addage(ee,headd,jss,lt[e[i].u],lt[e[i].v]),cd[lt[e[i].u]]++,rd[lt[e[i].v]]++;
    for(int i=1;i<=lts;++i)
        if(rd[i]==0)q.push(i),f[i]=ltn[i],ff[i]=1;
    dfs();
    for(int i=1;i<=lts;++i)
    {
        if(f[i]==maxd)maxf=(maxf+ff[i])%x;
    }
    printf("%d\n%d\n",maxd,maxf);
    return 0;
}