【tarjan+拓扑】BZOJ3887-[Usaco2015 Jan]Grass Cownoisseur

【题目大意】

给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1)

【思路】

首先缩点,对于每一个联通块求出正图和反图中节点1所在的联通块到它的最长节点数。这个用拓扑排序处理一下。

枚举每一条边取反,对于边(u,v),其取反后的距离就等于dis[u所在的联通快]+dis[v所在的联通块]-dis[1所在的联通块](因为会被重复计算不要忘记减去)

我一开始非常脑抽地在想会不会发生这样的情况:本来到u所在的联通块就会经过v,这样不就重复计算点了。要注意缩点之后的图为DAG,如果存在v->u的路径,同时存在u->v的路径,那么必定存在环,矛盾。

【错误点】

写x节点所在的联通块的时候,一直写成x节点。千万不要忘记了col[]。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<stack>
  7 #include<queue>
  8 using namespace std;
  9 const int MAXN=100000+50;
 10 vector<int> E[MAXN];
 11 vector<int> tE[MAXN],rtE[MAXN];
 12 stack<int> S;
 13 int x[MAXN],y[MAXN];
 14 int instack[MAXN],low[MAXN],dfn[MAXN],col[MAXN],size[MAXN],cnt,colcnt;
 15 int dis[MAXN],rdis[MAXN],degree1[MAXN],degree2[MAXN];
 16 int vis[MAXN];
 17 int n,m;
 18
 19 void Topology1()
 20 {
 21     memset(dis,0xef,sizeof(dis));//初始化为-INF
 22     queue<int> que;
 23     dis[col[1]]=size[col[1]];
 24     for (int i=1;i<=colcnt;i++)
 25         if (!degree1[i]) que.push(i);
 26     while (!que.empty())
 27     {
 28         int now=que.front();que.pop();
 29         for (int i=0;i<tE[now].size();i++)
 30         {
 31             int to=tE[now][i];
 32             dis[to]=max(dis[to],dis[now]+size[to]);
 33             if (!--degree1[to]) que.push(to);
 34         }
 35     }
 36 }
 37
 38 void Topology2()
 39 {
 40     memset(rdis,0xef,sizeof(rdis));
 41     queue<int> que;
 42     rdis[col[1]]=size[col[1]];
 43     for (int i=1;i<=colcnt;i++)
 44         if (!degree2[i]) que.push(i);
 45     while (!que.empty())
 46     {
 47         int now=que.front();que.pop();
 48         for (int i=0;i<rtE[now].size();i++)
 49         {
 50             int to=rtE[now][i];
 51             rdis[to]=max(rdis[to],rdis[now]+size[to]);
 52             if (!--degree2[to]) que.push(to);
 53         }
 54     }
 55 }
 56
 57
 58 void tarjan(int u)
 59 {
 60     dfn[u]=low[u]=++cnt;
 61     instack[u]=1;
 62     S.push(u);
 63     for (int i=0;i<E[u].size();i++)
 64     {
 65         int v=E[u][i];
 66         if (!instack[v])
 67         {
 68             tarjan(v);
 69             low[u]=min(low[u],low[v]);
 70
 71         }
 72         else if (instack[v]==1) low[u]=min(low[u],dfn[v]);
 73     }
 74
 75     if (dfn[u]==low[u])
 76     {
 77         colcnt++;
 78         int x;
 79         do
 80         {
 81             x=S.top();
 82             col[x]=colcnt;
 83             instack[x]=2;
 84             size[colcnt]++;
 85             S.pop();
 86         }while (x!=u);
 87     }
 88 }
 89
 90 void init()
 91 {
 92     scanf("%d%d",&n,&m);
 93     for (int i=0;i<m;i++)
 94     {
 95         scanf("%d%d",&x[i],&y[i]);
 96         E[x[i]].push_back(y[i]);
 97     }
 98     memset(instack,0,sizeof(instack));
 99     cnt=colcnt=0;
100     for (int i=1;i<=n;i++)
101         if (!instack[i]) tarjan(i);
102     for (int i=0;i<m;i++)
103     {
104         if (col[x[i]]!=col[y[i]])
105         {
106             tE[col[x[i]]].push_back(col[y[i]]);
107             degree1[col[y[i]]]++;
108             rtE[col[y[i]]].push_back(col[x[i]]);
109             degree2[col[x[i]]]++;
110         }
111     }
112 }
113
114 void solve()
115 {
116     memset(dis,0,sizeof(dis));
117     memset(rdis,0,sizeof(rdis));
118     Topology1();
119     Topology2();
120     int ans=-1;
121     for (int i=0;i<m;i++)
122     {
123         ans=max(ans,dis[col[x[i]]]+rdis[col[y[i]]]);//注意这里是col[x[i]]不要写成x[i]了
124         ans=max(ans,rdis[col[x[i]]]+dis[col[y[i]]]);
125     }
126     printf("%d",ans-size[1]);
127 }
128
129 int main()
130 {
131     init();
132     solve();
133     return 0;
134 }
时间: 2024-08-03 18:21:38

【tarjan+拓扑】BZOJ3887-[Usaco2015 Jan]Grass Cownoisseur的相关文章

bzoj3887: [Usaco2015 Jan]Grass Cownoisseur

题意: 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1) =>有向图我们先考虑缩点.然后观察缩点后的图可以发现新的路径中必定只有一条边是反向的才符合条件.那么我们可以联想到某道最短路的题将边反向存一遍后分别从s和t跑一跑.那么这里bfs跑一跑就行了.然后有一个坑点:这种重建图的注意es和edges不然es会在中途就被修改掉了... #include<cstdio> #

BZOJ 3887[Usaco2015 Jan]Grass Cownoisseur

题面: 3887: [Usaco2015 Jan]Grass Cownoisseur Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 237  Solved: 130[Submit][Status][Discuss] Description In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow pat

BZOJ 3887 Usaco2015 Jan Grass Cownoisseur Tarjan+拓扑排序

题目大意:给定一张图,从1开始随便走最后回到1,有一次机会可以反向沿着某条边走一次,求最多能经过多少个点 显然如果没有反向的机会的话答案就是1号节点所在强连通分量的大小 现在有了这个机会 那么将某条边反向后 缩点之后的图形成了一个包含1号节点所在强连通分量的环 这样才能使答案增加 将这个环从反向的边和1号节点所在强连通分量处断开 发现这个环被拆成了两条链 一条从1出发,一条指向1 因此缩点后利用拓扑排序分别求出正图和反图中1号节点所在强连通分量到每个强连通分量的最长链 然后枚举每条边反转更新答案

[USACO15JAN]草鉴定Grass Cownoisseur(分层图+tarjan)

[USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow paths all over his farm. The farm consists of N fields, conveniently numbered 1..N, with each one-way cow path

洛谷——P3119 [USACO15JAN]草鉴定Grass Cownoisseur

P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow paths all over his farm. The farm consists of N fields, conveniently numbered 1..N, with each one-way co

P3119 [USACO15JAN]草鉴定Grass Cownoisseur

P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-way cow paths all over his farm. The farm consists of N fields, conveniently numbered 1..N, with each one-way co

洛谷 P3119 [USACO15JAN]草鉴定Grass Cownoisseur

P3119 [USACO15JAN]草鉴定Grass Cownoisseur tarjan缩点,正反spfa,枚举边,更新最大值 1 #include<bits/stdc++.h> 2 using namespace std; 3 #define maxn 1000000 4 #define inf 0x3f3f3f3f 5 int n,m,x[maxn],y[maxn],z,num,head[maxn],head2[maxn],tim,ans,tot,dis1[maxn],dis2[maxn

[USACO15JAN]草鉴定Grass Cownoisseur

[题目描述]: [USACO15JAN]草鉴定Grass Cownoisseur [思路]: 首先我们先思考贝茜不走那条反边,那么对于任意强连通分量\(E\)易知: \(\forall u,v \in E\),\(\exists u \to v \ and \ v \to u\) \(\because\)贝茜每次经过一个草场时只会吃一次草,\(\therefore\)可以进行缩点,缩点后得到一个\(DAG\),统计每一个强连通分量的\(size\)值,表示此强连通分量中有多少个点,然后在\(DA

3890: [Usaco2015 Jan]Meeting Time( dp )

简单的拓扑图dp.. A(i, j), B(i, j) 表示从点 i 长度为 j 的两种路径是否存在. 用bitset就行了 时间复杂度O(m) ---------------------------------------------------------------- #include<bits/stdc++.h> #define clr(x, c) memset(x, c, sizeof(x)) #define rep(i, n) for(int i = 0; i < n; ++