zoukankan      html  css  js  c++  java
  • [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
    %d",sum[ans1],ans2);
    }
    
  • 相关阅读:
    【题解】 P1373 小a和uim之大逃离
    题解 CF576C 【Points on Plane】
    题解 P4799 【[CEOI2015 Day2]世界冰球锦标赛】
    【题解】[JSOI2008]最大数
    题解 P3389 【【模板】高斯消元法】
    【模板】矩阵加速
    【模板】树状数组上的差分数组
    tarjan求强连通分量(模板)
    我好菜系列——map查找
    trie树的应用;
  • 原文地址:https://www.cnblogs.com/lsgjcya/p/9520563.html
Copyright © 2011-2022 走看看