Tarjan水题系列(1):草鉴定Grass Cownoisseur [USACO15JAN]or[luogu P3119]

题目如下:

约翰有n块草场,编号1到n,这些草场由若干条单行道相连。奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草。

贝西总是从1号草场出发,最后回到1号草场。她想经过尽可能多的草场,贝西在通一个草场只吃一次草,所以一个草场可以经过多次。因为草场是单行道连接,这给贝西的品鉴工作带来了很大的不便,贝西想偷偷逆向行走一次,但最多只能有一次逆行。问,贝西最多能吃到多少个草场的牧草。

 

 

大意:

求一个有向图在走一次反向边或不走的情况下走的节点数最多的回到起点的路径的节点数

 

 

思路:

首先边可以重复走 所以缩点不会影响答案

缩点后得到的是一个DAG 我们有走一次反向边的机会 此时就相当于在DAG上加一条边 由于要从起点回到起点 这加的一条边的两端要满足一端起点能够到达 一端能通往起点

说到这里算法已经显而易见了 故不赘述了

下面是代码:

  1 #include <cstdio>
  2 #include <iostream>
  3 #include <algorithm>
  4 #include <queue>
  5 #include <memory.h>
  6 #define r(x) x=read()
  7 #define MAXX 100005
  8 #define MIN(a,b) (a<b?a:b)
  9 #define MAX(a,b) (a>b?a:b)
 10 using namespace std;
 11 int h[MAXX],h2[MAXX],cnt,dis[3][MAXX],flag[MAXX];
 12 int dfn[MAXX],low[MAXX],id[MAXX],w[MAXX],k,top,sta[MAXX],num;
 13 int n,m,ans,ans2,u,to,st;
 14 struct edge{int to,nex;}e[MAXX],yuan[MAXX];
 15 void add(int u,int to)
 16 {
 17   cnt++;
 18   e[cnt]=(edge){to,h[u]},h[u]=cnt;
 19 }
 20 void tarjan(int now)
 21 {
 22   sta[++top]=now;
 23   dfn[now]=low[now]=++k;
 24   for(int i=h[now];i;i=e[i].nex)
 25   {
 26     if(!dfn[e[i].to])
 27       tarjan(e[i].to),low[now]=MIN(low[now],low[e[i].to]);
 28     else
 29       if(!id[e[i].to])
 30         low[now]=MIN(low[now],dfn[e[i].to]);
 31   }
 32   if(low[now]==dfn[now])
 33   {
 34     id[now]=++num;
 35     while(sta[top]!=now){id[sta[top]]=num;top--;}
 36     top--;
 37   }
 38 }
 39 void spfa(int now,int x)
 40 {
 41   queue<int>que;
 42   dis[x][now]=w[now];
 43   que.push(now);
 44   flag[now]=1;
 45   while(!que.empty())
 46   {
 47     int p=que.front();que.pop();
 48     for(int i=h[p];i;i=e[i].nex)
 49     {
 50       if(i%2!=x) continue;
 51       if(dis[x][e[i].to]<dis[x][p]+w[e[i].to])
 52       {
 53         dis[x][e[i].to]=dis[x][p]+w[e[i].to];
 54         if(!flag[e[i].to])
 55           flag[e[i].to]=1,que.push(e[i].to);
 56       }
 57     }
 58     flag[p]=0;
 59   }
 60 }
 61 void dfs(int now)
 62 {
 63   for(int i=h[now];i;i=e[i].nex)
 64   {
 65     int to=e[i].to;
 66     if(i%2==0)
 67     {
 68       if(dis[1][now]&&dis[0][to]) 
 69         ans=MAX(ans,dis[1][now]+dis[0][to]);
 70     }
 71     else
 72       dfs(to);
 73   }
 74 }
 75 bool cmp(edge a,edge b)
 76 {
 77   return id[a.to]==id[b.to]?id[a.nex]<id[b.nex]:id[a.to]<id[b.to];
 78 }
 79 int read()
 80 {
 81   char ch=0;int w=0;
 82   while(ch<'0'||ch>'9'){ch=getchar();}
 83   while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
 84   return w;
 85 }
 86 int main()
 87 {
 88   r(n),r(m);
 89   for(int i=1;i<=m;++i)
 90     r(u),r(to),add(u,to),yuan[i]=(edge){to,u};
 91   for(int i=1;i<=n;++i)
 92     if(!dfn[i])
 93       tarjan(i);
 94   for(int i=1;i<=n;++i)
 95     w[id[i]]++;
 96   st=id[1];
 97   sort(yuan+1,yuan+m+1,cmp);
 98   memset(h,0,sizeof(h));
 99   cnt=0;
100   for(int i=1;i<=m;++i)
101   {
102     int x=id[yuan[i].nex],y=id[yuan[i].to];
103     if((x==id[yuan[i-1].nex]&&y==id[yuan[i-1].to])||(x==y)) 
104       continue;
105     add(x,y),add(y,x);
106   }
107   spfa(st,1),spfa(st,0);
108   ans=w[st];
109   dfs(st);
110   if(ans>w[st]) printf("%d",ans-w[st]);
111   else printf("%d",ans);
112   return 0;
113 }

 

上一篇:Kosaraju与Tarjan(图的强连通分量)


下一篇:洛谷P4742(tarjan缩点+拓扑DP)