码迷,mamicode.com
首页 > 其他好文 > 详细

最大半连通子图

时间:2019-10-23 20:36:58      阅读:126      评论:0      收藏:0      [点我收藏+]

标签:pre   rebuild   set   记录   个数   代码   ext   top   turn   

https://loj.ac/problem/10092

题目描述

  给出一张图,定义图的半连通子图为点集S中任意两点u、v都存在一条u到v的简单路径或v到u的简单路径,求这张图的最大半连通子图的节点数和个数。

思路

  首先显然一个强连通子图一定是半连通子图,所以我们可以先进行缩点,这样并不影响结果的判定。接下来考虑缩点后的DAG,最大半连通子图可以转化为DAG上最长链的长度,对于这个我们可以用类似最短路方法,不过由于是DAG我们可以直接进行拓扑排序时更新最短路的值,接下来再类似最短路计数用sum[i]记录到i的路径个数。

  不过需要注意虽然原图中保证五重边,但缩点可能两个强连通分量间有多个有向边相连接,由于半连通子图时点集,与边无关,这会影响到最大半连通子图的计数,所以要去重。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10,M=1e6+10;

struct Edge
{
    int x,y;
}e[M];

bool cmp(Edge a,Edge b)
{
    if(a.x!=b.x)return a.x<b.x;
    else return a.y<b.y;
}

int head[N],nxt[M],to[M],tot;
int n,m;
void add_edge(int x,int y)
{
    nxt[++tot]=head[x];
    head[x]=tot;
    to[tot]=y;
}

int read()
{
    int res=0,w=1;
    char ch=getchar();
    while(ch<0||ch>9){if(ch==-)w=-1;ch=getchar();}
    while(ch>=0&&ch<=9){res=(res<<3)+(res<<1)+ch-0;ch=getchar();}
    return res*w;
}

int st[N],siz[N],dfn[N],low[N],idx,co[N],top,col;
void tarjan(int u)
{
    dfn[u]=low[u]=++idx;
    st[++top]=u;
    for(int i=head[u];i;i=nxt[i])
    {
        int v=to[i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else if(!co[v])
            low[u]=min(low[u],dfn[v]);
    }
    if(low[u]==dfn[u])
    {
        co[u]=++col;
        ++siz[col];
        while(st[top]!=u)
        {
            ++siz[col];
            co[st[top]]=col;
            --top;
        }
        --top;
    }
}

void del()    //删边 
{
    for(int i=1;i<=m;i++)
    {
        int u=e[i].x,v=e[i].y;
        e[i].x=co[u];e[i].y=co[v];
    }
    sort(e+1,e+m+1,cmp);
}

int in[N];
void rebuild()    //重新建图 
{
    del(); 
    tot=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        int u=e[i].x,v=e[i].y;
        if(u!=v&&(u!=e[i-1].x||v!=e[i-1].y))
        {
            add_edge(u,v);
            ++in[v];
        }
    }
}

int dis[N],sum[N],ans,mod;
queue<int> q;
void topo()
{
    for(int i=1;i<=col;i++)
        if(in[i]==0)
        {
            q.push(i);
            dis[i]=siz[i];
            sum[i]=1;
            if(dis[ans]<dis[i])ans=i;
        }
    while(!q.empty())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=nxt[i])
        {
            int v=to[i];
            --in[v];
            if(!in[v])q.push(v);
            if(dis[v]<dis[u]+siz[v])
            {
                dis[v]=dis[u]+siz[v];
                sum[v]=0;
                if(dis[ans]<dis[v])ans=v;
            }
            if(dis[v]==dis[u]+siz[v])
                sum[v]=(sum[v]+sum[u])%mod;
            
        }
    }
}

int main() 
{
    n=read();m=read();mod=read();
    for(int i=1;i<=m;i++)
    {
        int a=read(),b=read();
        e[i].x=a;e[i].y=b;
        add_edge(a,b);
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])tarjan(i);
    rebuild();
    topo();
    int cnt=0;
    for(int i=1;i<=col;i++)
        if(dis[ans]==dis[i])cnt=(cnt+sum[i])%mod;
    printf("%d\n%d",dis[ans],cnt);
    return 0;
}

 

最大半连通子图

标签:pre   rebuild   set   记录   个数   代码   ext   top   turn   

原文地址:https://www.cnblogs.com/fangbozhen/p/11728155.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!