[ZJOI2007]最大半连通子图(Tarjan,拓扑序DP)

[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的余数。

输入输出格式

输入格式:

第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。

输出格式:

应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

输入输出样例

输入样例#1:

6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4

输出样例#1:

3
3

说明

对于100%的数据, \(N \le 100000, M \le 1000000, X \le 10^8\)

Tarjan+拓扑序DP

在一个强联通分量里的点可以互相到达,所以我们先缩点,减少处理情况。
现在图变成了一张有向无环图,对于求出最多的节点数显然可以用拓扑排序求出,而对于第二问的方案数写个DP即可,具体见代码。
最近总出数组开小这种蛇皮错误qwq。

#include<bits/stdc++.h>
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
using namespace std;
int read()
{
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*w;
}
const int N=100010;
int n,m,cnt,mod,top,visnum,scc,ans1,ans2,qwe,jishu;
int head[N],x[10*N],y[10*N],du[N];
int dfn[N],low[N],s[N],num[N],sum[N],belong[N],dp[N];
bool in[N];
struct node{
    int to,next;
}edge[20*N];
struct Node{
    int x,y;
}f[10*N];
queue<int>q;
bool cmp(Node p,Node q){if(p.x==q.x)return p.y<q.y;return p.x<q.x;}
void add(int x,int y)
{
    cnt++;edge[cnt].to=y;edge[cnt].next=head[x];head[x]=cnt;
}
void tarjan(int k)
{
    int v;
    dfn[k]=low[k]=++visnum;
    s[++top]=k;in[k]=1;
    for(int i=head[k];i;i=edge[i].next)
    {
        v=edge[i].to;
        if(!dfn[v])
        {
            tarjan(v);low[k]=Min(low[k],low[v]);
        }
        else if(in[v]) low[k]=Min(low[k],dfn[v]);
    }
    if(dfn[k]==low[k])
    {
        v=-1;scc++;
        while(v!=k)
        {
            v=s[top--];in[v]=0;num[scc]++;belong[v]=scc;
        }
    }
}
int main()
{
    n=read();m=read();mod=read();
    for(int i=1;i<=m;i++)
    {
        x[i]=read();y[i]=read();add(x[i],y[i]);
    }
    for(int i=1;i<=n;i++)
    {
        if(!dfn[i]) tarjan(i);
    }
    cnt=0;memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++)
    {
        int xx=belong[x[i]],yy=belong[y[i]];
        if(xx!=yy) {qwe++;f[qwe].x=xx;f[qwe].y=yy;}
    }
    sort(f+1,f+1+qwe,cmp);
    for(int i=1;i<=qwe;i++)
    {
        if(f[i].x==f[i-1].x&&f[i].y==f[i-1].y) continue;
        add(f[i].x,f[i].y);du[f[i].y]++;
    }
    for(int i=1;i<=scc;i++)
    {
        if(!du[i]) q.push(i),sum[i]=num[i],dp[i]=1;
    }
    while(q.size())
    {
        int u=q.front();q.pop();jishu++;
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            du[v]--;			
            if(sum[v]<sum[u]+num[v]) sum[v]=sum[u]+num[v],dp[v]=dp[u];
            else if(sum[v]==sum[u]+num[v]) dp[v]+=dp[u];
            dp[v]%=mod;
            if(!du[v]) q.push(v);
        }
    }
    for(int i=1;i<=scc;i++)
    {
        if(sum[ans1]<sum[i]) ans1=i,ans2=dp[i];
        else if(sum[ans1]==sum[i]) ans2+=dp[i];
        ans2%=mod;
    }
    printf("%d\n%d",sum[ans1],ans2);
}
posted @ 2018-08-22 21:40  Frozen_Heart  阅读(157)  评论(0编辑  收藏  举报