zoukankan      html  css  js  c++  java
  • BZOJ1093 [ZJOI2007]最大半连通子图

    Description

      一个有向图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的余数。

    Input

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

    Output

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

    Sample Input

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

    Sample Output

    3
    3
    这道题首先用tarjan缩点,因为强连通分量必然能放在一个半连通子图里。
    之后图变为DAG 拓扑排序dp即可。
    f[i]表示到i的最大半连通子图,sl[i]表示i这个强连通分量的点数,sum[i]表示方案数%k。
    f[v]=max(f[u]+sl[v])u为所有能到达v的点。
    sum[v]=sigema(sum[u])f[u]+sl[v]==f[v]
    这里要注意,两点间可能有重边,要做判断防止一个点被加两遍。
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    struct X
    {
        int v,n,f;
    }x[1000005],y[1000005];
    const int N=1e5+5;
    int s,st[N],top,dfn[N],low[N],pa[N],cnt,t,w=-1,q[N],syg[N];
    bool vis[N],sf[N];
    void add(int u,int v)
    {
        y[++s].n=y[u].f;
        y[y[u].f=s].v=v;
    }
    void dfs(int u)
    {
        dfn[u]=low[u]=++s;
        vis[st[++top]=u]=sf[u]=1;
        for(int i=x[u].f;i;i=x[i].n)
            if(!vis[x[i].v]) dfs(x[i].v),low[u]=min(low[x[i].v],low[u]);
            else if(sf[x[i].v]) low[u]=min(low[u],dfn[x[i].v]);
        if(dfn[u]==low[u])
        {
            pa[u]=++cnt;
            for(;st[top]!=u;top--)
                pa[st[top]]=cnt,sf[st[top]]=0;
            top--;sf[u]=0;
        }
    }
    int main()
    {
        int n,m,mod;
        scanf("%d%d%d",&n,&m,&mod);
        for(int i=1;i<=m;i++)
        {
            int u;
            scanf("%d%d",&u,&x[i].v);
            x[i].n=x[u].f;
            x[u].f=i;
        }
        for(int i=1;i<=n;i++)
            if(!vis[i]) dfs(i);
        memset(st,0,sizeof(st));
        memset(dfn,0,sizeof(dfn));
        memset(vis,0,sizeof(vis));
        s=0;
        for(int i=1;i<=n;i++)
        {
            st[pa[i]]++;
            for(int j=x[i].f;j;j=x[j].n)
                if(pa[i]!=pa[x[j].v]) add(pa[i],pa[x[j].v]),++dfn[pa[x[j].v]];
        }
        memset(pa,0,sizeof(pa));
        memset(low,0,sizeof(low));
        for(int i=1;i<=cnt;i++)
            if(!dfn[i]) vis[q[++w]=i]=1,low[i]=1,pa[i]=st[i];
        for(;t<=w;t++)
        {
            for(int i=y[q[t]].f;i;i=y[i].n)
                if(!vis[y[i].v])
                {
                    dfn[y[i].v]--;
                    if(!dfn[y[i].v]) vis[q[++w]=y[i].v]=1;
                    if(syg[y[i].v]==q[t]) continue;
                    syg[y[i].v]=q[t];
                    if(st[y[i].v]+pa[q[t]]>pa[y[i].v])
                    {
                        pa[y[i].v]=pa[q[t]]+st[y[i].v];
                        low[y[i].v]=low[q[t]];
                    }
                    else if(st[y[i].v]+pa[q[t]]==pa[y[i].v]) low[y[i].v]+=low[q[t]],low[y[i].v]%=mod;
                }
        }
        int ans1=0,ans2;
        for(int i=1;i<=cnt;i++)
            if(ans1<pa[i]) ans1=pa[i],ans2=low[i];
            else if(ans1==pa[i]) ans2+=low[i],ans2%=mod;
        printf("%d
    %d",ans1,ans2);
        return 0;
    }
  • 相关阅读:
    Leetcode Substring with Concatenation of All Words
    Leetcode Divide Two Integers
    Leetcode Edit Distance
    Leetcode Longest Palindromic Substring
    Leetcode Longest Substring Without Repeating Characters
    Leetcode 4Sum
    Leetcode 3Sum Closest
    Leetcode 3Sum
    Leetcode Candy
    Leetcode jump Game II
  • 原文地址:https://www.cnblogs.com/bzmd/p/6240925.html
Copyright © 2011-2022 走看看