bzoj 1093 最大半连通子图

这个题一定要把题意弄清楚,然后就是发现可以缩点搞,缩点了以后这个最大半连通子图就是一条最长的单向链。

然后用类似树型dp的方法(这里是BFS更新答案),可以把答案求出来。

注意缩点以后可能会有重边(参考proverbs大神的博客)如果用DFS更新dp的话,不好判断重边。

View Code
  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #define maxn 120000
  7 #define maxm 1200000
  8 #define inf 2147483646
  9 using namespace std;
 10 struct et
 11 {
 12     int s,t,next;
 13 }d[maxm],e[maxm];
 14 int f[maxn],g[maxn];
 15 int dfn[maxn],low[maxn],web[maxn],sum[maxn],q[maxn];
 16 int fir[maxn],last[maxn],v[maxn],id[maxn],stack[maxn],vis[maxn];
 17 int n,m,tot,tt,num,ms,tim,top;
 18 
 19 inline void insert(int x,int y)
 20 {
 21     e[++tot].s=x; e[tot].t=y; e[tot].next=fir[x]; fir[x]=tot;
 22 }
 23 inline void add(int x,int y)
 24 {
 25     d[++tt].s=x; d[tt].t=y; d[tt].next=last[x]; last[x]=tt;
 26 }
 27 
 28 inline void tarjan(int now)
 29 {
 30     dfn[now]=low[now]=++tim;
 31     stack[++top]=now;
 32     v[now]=1;
 33     for (int j=last[now];j;j=d[j].next)
 34     {
 35         int k=d[j].t;
 36         if (!v[k]) tarjan(k);
 37         if (v[k]<2) low[now]=min(low[now],low[k]);
 38     }
 39     if (dfn[now]==low[now])
 40     {
 41         num++;
 42         while (stack[top+1]!=now)
 43         {
 44             v[stack[top]]=2;
 45             web[stack[top]]=num;
 46             sum[num]++;
 47             --top;
 48         }
 49     }
 50 }
 51 
 52 inline void dfs(int now)
 53 {
 54     v[now]=1;
 55     for (int j=last[now];j;j=d[j].next)
 56     {
 57         int k=d[j].t;
 58         if (web[now]!=web[k]) insert(web[now],web[k]),++id[web[k]];
 59         if (!v[k]) dfs(k);
 60     }
 61 }
 62 
 63 inline void find()
 64 {
 65     memset(vis,0,sizeof(vis));
 66     int head=0,tail=0;
 67     for (int i=1;i<=num;i++) 
 68         if (!id[i]) q[++tail]=i,f[i]=sum[i],g[i]=1;
 69     while (head<tail)
 70     {
 71         int now=q[++head];
 72         for (int j=fir[now];j;j=e[j].next)
 73         {
 74             int k=e[j].t;
 75             if (!(--id[k])) q[++tail]=k;
 76             if (vis[k]==now) continue;
 77             if (f[now]+sum[k]>f[k]) f[k]=f[now]+sum[k],g[k]=g[now];
 78             else
 79             if (f[now]+sum[k]==f[k]) g[k]=(g[k]+g[now])%ms;
 80             vis[k]=now;
 81         }
 82     }
 83 }
 84 
 85 int main()
 86 {
 87     //freopen("semi.in","r",stdin);
 88     scanf("%d %d %d",&n,&m,&ms);
 89     int x,y;
 90     for (int i=1;i<=m;i++)
 91         scanf("%d %d",&x,&y),add(x,y);
 92     for (int i=1;i<=n;i++)
 93         if (!v[i]) tarjan(i);
 94     memset(v,0,sizeof(v));
 95     for (int i=1;i<=n;i++) 
 96         if (!v[i]) dfs(i);
 97     find();
 98     int ans=0,fig=0;
 99     for (int i=1;i<=num;i++)
100     {
101         if (f[i]>ans) ans=f[i],fig=g[i];
102         else
103         if (f[i]==ans) fig=(fig+g[i])%ms;
104     }
105     printf("%d\n",ans);
106     printf("%d\n",fig);
107     return 0;
108 }

 

转载于:https://www.cnblogs.com/zig-zag/archive/2013/03/26/2983103.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值