[BZOJ1093][ZJOI2007][Tarjan][DP]最大半联通子图

[题目]

[算法]

Tarjan+DP

[分析]

易证一个强连通分量一定在最大半联通子图里面,所以先用tarjan缩点,然后图就变成了一个有向无环图。在这上面用(拓扑排序)dp的方法求出最长的链以及个数就可以了。但是要注意两个点(tarjan后)之间可能有多条边,而根据题目的意思这多条边只算一次方案(因为是导出子图),所以要注意判重(记录每个点上一次访问它的是谁,如果和当前点一样就跳过)

[代码]

/**************************************************************
    Problem: 1093
    User: gaotianyu1350
    Language: C++
    Result: Accepted
    Time:2196 ms
    Memory:33544 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <queue>
using namespace std;
 
#define MAXN 100100
#define MAXM 1001000
 
struct graph
{
     int point[MAXN],next[MAXM],v[MAXM];
     int tot;
     graph()
     {
        memset(point,0,sizeof(point));
        memset(next,0,sizeof(next));
        tot=0;
     }
     void addedge(int x,int y)
     {
        tot++;
        next[tot]=point[x];point[x]=tot;v[tot]=y;
     }
};
 
graph ori,last,relast;
int dfsTime[MAXN]={0},lowTime[MAXN]={0},st[MAXN]={0},duiying[MAXN]={0};
int rudu[MAXN]={0};
int f[MAXN]={0},fcnt[MAXN]={0};
int size[MAXN]={0};
int ctDfsTime=0,ctConnect=0,stTop=0;
int n,m,MOD;
int vis[MAXN]={0};
queue<int> q;
 
void CheckMemory()
{
    printf("%d\n",(sizeof(ori)*3+sizeof(dfsTime)+sizeof(lowTime)
           +sizeof(st)+sizeof(duiying)+sizeof(rudu)+sizeof(f)+sizeof(fcnt)
           +sizeof(size))/1000000);
}
 
void Tarjan(int now)
{
    ctDfsTime++;
    dfsTime[now]=lowTime[now]=ctDfsTime;
    st[++stTop]=now;
    for (int temp=ori.point[now];temp;temp=ori.next[temp])
        if (!lowTime[ori.v[temp]])
        {
            Tarjan(ori.v[temp]);
            lowTime[now]=min(lowTime[now],lowTime[ori.v[temp]]);
        }
        else
            if (!duiying[ori.v[temp]])
                lowTime[now]=min(lowTime[now],dfsTime[ori.v[temp]]);
         
    if (dfsTime[now]==lowTime[now])
    {
        ctConnect++;
        while (st[stTop]!=now)
            size[ctConnect]++,duiying[st[stTop--]]=ctConnect;
        stTop--;
        duiying[now]=ctConnect,size[ctConnect]++;
    }
}
  
void Rebuild()
{
    for (int i=1;i<=n;i++)
        for (int temp=ori.point[i];temp;temp=ori.next[temp])
            if (duiying[i]!=duiying[ori.v[temp]])
            {
                last.addedge(duiying[i],duiying[ori.v[temp]]);
                rudu[duiying[ori.v[temp]]]++;
                relast.addedge(duiying[ori.v[temp]],duiying[i]);
            }
    for (int i=1;i<=ctConnect;i++)
        if (!rudu[i])
        {
            q.push(i);
            fcnt[i]=1;
            f[i]=size[i];
        }
}
 
void Dp()
{
    int maxAns=0;
    int cntAns=0;
    while (!q.empty())
    {
        int now=q.front();q.pop();
        for (int temp=relast.point[now];temp;temp=relast.next[temp])
        {
            int vv=relast.v[temp];
            if (vis[vv]==now) continue;
            if (f[vv]+size[now]>f[now])
            {
                f[now]=f[vv]+size[now];fcnt[now]=fcnt[vv]%MOD;
            }
            else
                if (f[vv]+size[now]==f[now]) fcnt[now]=(fcnt[now]+fcnt[vv])%MOD;
            vis[vv]=now;
        }
        for (int temp=last.point[now];temp;temp=last.next[temp])
        {
            rudu[last.v[temp]]--;
            if (!rudu[last.v[temp]]) q.push(last.v[temp]);
        }
        if (f[now]>maxAns)
        {
            maxAns=f[now];cntAns=fcnt[now]%MOD;
        }
        else if (f[now]==maxAns)
            cntAns=(cntAns+fcnt[now])%MOD;
    }
    printf("%d\n%d\n",maxAns,cntAns);
}
 
int main()
{
    //freopen("input.txt","r",stdin);
    //CheckMemory();
    scanf("%d%d%d",&n,&m,&MOD);
    for (int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ori.addedge(x,y);
    }
    for (int i=1;i<=n;i++)
        if (!duiying[i]) 
            Tarjan(i);
    Rebuild();
    Dp();
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值