zoukankan      html  css  js  c++  java
  • [BZOJ]1093 最大半连通子图(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)不会出现两次。

    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

    HINT

      N ≤10000, M ≤1000000,X ≤10^8。

    Solution

      拿到这题,我们首先思考半连通分量是个什么东西。

      首先我们知道,强连通分量一定是半连通分量,

      题目要求我们求最大的半连通分量,所以如果选取了一个强连通分量里的点,那么把该点所在的整个强连通分量都选进去肯定没问题,选取一个强连通分量和选取一个点是等价的。

      所以我们很自然地用tarjan缩了缩点……

      然后我们得到了一个带点权的拓扑图。

      仔细一想,我们发现拓扑图中的半连通分量是一条链,

      所以问题也就变成了找拓扑图中的最长链,并统计最长链的条数。

      (这个用DP不会做你退群吧)

      注意缩点之后要处理掉重边。时间复杂度O(n+m)。

    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #define MN 100005
    #define MM 1000005
    using namespace std;
    struct edge{int nex,to;}e[MM];
    struct bian{int x,y;}b[MM];
    bool u[MN],ink[MN];
    int low[MN],st[MN],hr[MN],bel[MN],w[MN],d[MN],q[MN],f1[MN],f2[MN];
    int dfn,pin,tp,n,m,mod,hd,tl,ans1,ans2;
    char B[1<<20],*SS,*TT;
    
    inline char getc() {return SS==TT&&(TT=(SS=B)+fread(B,1,1<<20,stdin),SS==TT)?EOF:*SS++;}
    inline int read()
    {
        register int n=0; char c;
        do c=getc(); while (c<'0' || c>'9');
        do n=n*10+c-'0',c=getc(); while (c>='0' && c<='9');
        return n;
    }
    
    inline void ins(int x,int y) {e[++pin]=(edge){hr[x],y}; hr[x]=pin;}
    
    void tarjan(int x)
    {
        register int i,lt;
        low[x]=lt=++dfn;
        u[x]=ink[x]=true; st[++tp]=x;
        for (i=hr[x];i;i=e[i].nex)
        {
            if (u[e[i].to]&&!ink[e[i].to]) continue;
            if (!u[e[i].to]) tarjan(e[i].to);
            low[x]=min(low[x],low[e[i].to]);
        }
        if (low[x]==lt)
            for (;st[tp+1]!=x;--tp) ink[st[tp]]=false,bel[st[tp]]=x,++w[x];
    }
    
    int main()
    {
        register int i,x;
        n=read(); m=read(); mod=read();
        for (i=1;i<=m;++i) b[i].x=read(),b[i].y=read(),ins(b[i].x,b[i].y);
        for (i=1;i<=n;++i) if (!u[i]) tarjan(i);
        memset(hr,0,sizeof(hr)); pin=0;
        for (i=1;i<=m;++i)
            if (bel[b[i].x]!=bel[b[i].y])
                ins(bel[b[i].x],bel[b[i].y]),++d[bel[b[i].y]];
        for (i=hd=1;i<=n;++i) if (bel[i]==i&&!d[i]) q[++tl]=i,f1[i]=w[i],f2[i]=1;
        for (;hd<=tl;++hd)
        {
            for (x=q[hd],i=hr[x];i;i=e[i].nex)
            {
                if (!--d[e[i].to]) q[++tl]=e[i].to;
                if (!u[e[i].to]) continue; else u[e[i].to]=false;
                if (f1[x]+w[e[i].to]>f1[e[i].to]) f1[e[i].to]=f1[x]+w[e[i].to],f2[e[i].to]=f2[x];
                else if (f1[x]+w[e[i].to]==f1[e[i].to]) f2[e[i].to]+=f2[x],f2[e[i].to]-=f2[e[i].to]>=mod?mod:0;
            }
            for (i=hr[x];i;i=e[i].nex) u[e[i].to]=true;
        }
        for (i=1;i<=n;++i)
            if (f1[i]>ans1) ans1=f1[i],ans2=f2[i];
            else if (f1[i]==ans1) ans2+=f2[i],ans2-=ans2>=mod?mod:0;
        printf("%d
    %d",ans1,ans2);
    }

    Last Word

      有向图用tarjan缩完点得到的是拓扑图,无向图缩环会变成树(森林)。

      用了n+e光速读入后立竿见影地卡到了这道题的rank2。(代码画风崩坏不可避)

  • 相关阅读:
    repeater嵌套RadioButtonList赋值
    hive表导出到mysql报错
    hive创建表时报错
    linux————mysql————修改密码
    Spark入门:Spark运行架构(Python版)
    1、Spark简介(Python版)
    使用蒙特 ·卡罗方法计算圆周率近似值
    python运算符,内置函数简单使用
    numpy、pandas、scipy、matplotlib、jieba、 openpyxl、pillow的安装
    XML详解
  • 原文地址:https://www.cnblogs.com/ACMLCZH/p/7634863.html
Copyright © 2011-2022 走看看