bzoj1093【ZJOI2007】最大半联通子图

题意:http://www.lydsy.com/JudgeOnline/problem.php?id=1093

sol  :一开始理解错题意了QAQ,还莫名其妙写挂了QAQ,调了半天

   首先显然一个强联通分量肯定要么都属于最大半联通子图,要么都不属于

   所以先用tarjan缩点,重建后得到一个DAG

   之后我们可以发现,得到的答案一定是一条链,所以要求最长链的长度和数量

   直接dp即可,记得判重(我挂了好久QAQ)

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int Mx=1000010;
int n,m,p,maxn,ans,cnt,tag,top;
int dfn[Mx],low[Mx],val[Mx],belong[Mx],stk[Mx];
int in[Mx],f[Mx],g[Mx],vis[Mx];
bool instk[Mx];
int tot,head1[Mx],head2[Mx],nxt1[2*Mx],ver1[2*Mx],nxt2[2*Mx],ver2[2*Mx];
void add1(int x,int y)
{
    nxt1[++tot]=head1[x];
    ver1[tot]=y;
    head1[x]=tot;
}
void add2(int x,int y)
{
    nxt2[++tot]=head2[x];
    ver2[tot]=y;
    head2[x]=tot;
    in[y]++;
}
void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    stk[++top]=x,instk[x]=1;
    for(int i=head1[x];i;i=nxt1[i])
    {
        int y=ver1[i];
        if(!dfn[y])
            tarjan(y),
            low[x]=min(low[x],low[y]);
        else if(instk[y])
            low[x]=min(low[x],dfn[y]);
    }
    if(low[x]==dfn[x])
    {
        int now=0;tag++;
        while(now!=x)
        {
            now=stk[top--];
            instk[now]=0;
            val[tag]++;
            belong[now]=tag;
        }
    }
}
void rebuild()
{
    tot=0;
    for(int x=1;x<=n;x++)
        for(int i=head1[x];i;i=nxt1[i])
        {
            int y=ver1[i];
            if(belong[x]!=belong[y])
                add2(belong[x],belong[y]);
        }
}
void dp()
{
    int l=0,r=0;
    for(int i=1;i<=tag;i++)
    {
        if(!in[i]) stk[r++]=i;
        f[i]=val[i],g[i]=1;
    }
    while(l!=r)
    {
        int x=stk[l++];
        for(int i=head2[x];i;i=nxt2[i])
        {
            int y=ver2[i]; in[y]--;
            if(!in[y]) stk[r++]=y;
            if(vis[y]==x) continue;
            if(f[x]+val[y]>f[y])
            {
                f[y]=f[x]+val[y];
                g[y]=g[x];
            }
            else if(f[x]+val[y]==f[y])
                g[y]=(g[x]+g[y])%p;
            vis[y]=x;
        }
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1,x,y;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        add1(x,y);
    }
    for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    rebuild(); dp();
    for(int i=1;i<=tag;i++)
    {
        if(f[i]>maxn) maxn=f[i],ans=g[i];
        else if(f[i]==maxn) ans+=g[i],ans%=p;
    }
    printf("%d\n%d\n",maxn,ans);
    return 0;
}

 

转载于:https://www.cnblogs.com/xiaoxubi/p/6473089.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值