[ZJOI2007] 最大半联通子图 最大半联通子图 题解 Code

题目描述

一个有向图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≤100000,M≤1000000,X≤10^8)

题解

bzoj1093

我们把题目所给的图画出来
[ZJOI2007] 最大半联通子图
最大半联通子图
题解
Code
题目要求求出一条路径,这条路径经过的点要最多,并输出这种路径的条数
可以发现,1,2两个点是可以互相到达的,那么就可以缩点,记录一下强连通分量内的点的个数,方便以后的操作

做到这里其实当时我是不知道接下来怎么做的,所以就打了个搜索
但是把缩点之后拿出来一看,突然就有了思路
因为原图是一个有向图,缩点之后就变成DAG,那么我们就可以topsort一下,确定我们记录的顺序,再然后发现可以dp

那么自然而然的最值就出来了,怎么统计方案数?
做过最短路计数的应该比较容易理解
在topsort中,对与一个从队列中取出的点u,和它可以到达的其中一个点v
如果被u更新的答案比原来v的答案更优,那么v的方案数就由u延续下来
也就是

if(dp[v]<dp[u]+num[v])//其中num[i]为i这个强连通分量内点的个数
dp[v]=dp[u]+num[v],cnt[v]=cnt[u]

如果被u更新的答案与v之前的答案一样,那么说明又多了一些方案可以到达这个答案

if(dp[v]==dp[u]+num[v]) 
cnt[v]+=cnt[u];

那么整个思路应该就出来了

  1. 对原图缩点
  2. 对新的DAG重新建边,注意一点,缩点直接建边会有很多重复的边,所以要去重
  3. 统计入度为0的点,并加入队列进行topsort并dp
  4. 统计答案

Code

#include<bits/stdc++.h>
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
using namespace std;
typedef long long lol;
const lol N=100010,M=1000010;

void in(lol &ans)
{
    ans=0;lol f=1;char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1;i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48),i=getchar();
    ans*=f;
}

stack<lol>s;
lol n,m,cnt,mod,dfscnt,scc,t;
lol ans,sum;
lol to[M<<1],nex[M<<1],head[N];
lol low[N],dfn[N],sccno[N],num[N],dp[N],dg[N],tq[N];

struct E{
    lol x,y;
}e[M],A[M];

inline void add(lol a,lol b) {
    to[++cnt]=b;
    nex[cnt]=head[a];
    head[a]=cnt;
}

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

void tarjan(lol u)
{
    dfn[u]= low[u] = ++dfscnt; s.push(u);
    for(lol i=head[u];i;i=nex[i]) 
        if(!dfn[to[i]]) tarjan(to[i]),low[u]=Min(low[u],low[to[i]]);
        else if(!sccno[to[i]]) low[u]=Min(low[u],dfn[to[i]]);
    if(low[u]==dfn[u]) {
        scc++;
        while(1) {
            lol x=s.top(); s.pop(); num[scc]++; sccno[x]=scc;
            if(x==u) break;
        }
    }
}

void topsort_dp()
{
    queue<lol>q;
    for(lol i=1;i<=scc;i++) if(!dg[i]) q.push(i),dp[i]=num[i],tq[i]=1;
    while(!q.empty()) {
        lol u=q.front(); q.pop();
        for(lol i=head[u];i;i=nex[i]) {
            if(dp[to[i]]<dp[u]+num[to[i]]) dp[to[i]]=dp[u]+num[to[i]],tq[to[i]]=tq[u];
            else if(dp[to[i]]==dp[u]+num[to[i]]) (tq[to[i]]+=tq[u])%=mod;
            if(!(--dg[to[i]])) q.push(to[i]);
        }
    }
}

int main()
{
    in(n), in(m) , in(mod);
    for(lol i=1;i<=m;i++) {
        lol a,b; in(a),in(b);
        add(a,b); e[i]=(E) {a,b};
    }
    for(lol 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 fx=sccno[e[i].x],fy=sccno[e[i].y];
        if(fx!=fy) A[++t] = (E) {fx,fy};
    }
    sort(A+1,A+1+t,cmp);
    for(lol i=1;i<=t;i++)
        if(A[i].x!=A[i-1].x || A[i].y!=A[i-1].y)
            add(A[i].x,A[i].y),dg[A[i].y]++;
    topsort_dp();
    for(int i=1;i<=scc;i++) {
        if(dp[i]>ans) {ans=dp[i],sum=tq[i];}
        else if(dp[i]==ans) (sum+=tq[i])%=mod;
    }
    printf("%lld
%lld
",ans,sum);
}

博主蒟蒻,随意转载.但必须附上原文链接

http://www.cnblogs.com/real-l/