题目描述

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

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

解析

此题就是在Tarjan的板子上玩了点花样,然鹅窝这种题都写不出来,看来我还需要提升。

首先容易看出来一个强连通分量里面草是随便吃而不会出现逆行的,所以我们先缩点。

以下我们考虑缩点后的图。

这个逆行一次回到起点的处理就比较麻烦了,我们不妨把这条路拆成两部分,分别是起点1号节点所在强连通分量到逆行边起点的最长路和逆行边终点回到起点的最长路。

我到这就不知道怎么处理逆行边终点回到起点的最长路了(科技树点歪)。试想如果直接拿逆行边终点跑最长路,T是妥妥的。

一个行之有效的解决方法是建反图,然后在反图上以1号节点所在强连通分量为起点跑最长路,得到的就是原图以1号节点所在强连通分量为终点的最长路,这个其实不难理解。

而且这样并不会导致吃两次草,即两次最长路的路径发生重叠,可以用反证法简单证明,这样的情况被缩点所排除了。

因为是DAG,所以最长路是可解的,我们SPFA或者拓扑去求就行了。

注意细节,我们对1号节点所在强连通分量是统计了两次的,最后要减去一次。

参考代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#define N 100010
#define INF 0x3f3f3f3f
#define IN freopen("data.in","r",stdin);
using namespace std;
inline int read()
{
int f=1,x=0;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*f;
}
struct rec{
int next,ver;
}g[N<<1],G[N],G2[N];
int head[N],tot,headG[N],totG,headG2[N],totG2,n,m,dfn[N],low[N];
int stack[N],top,scc[N],idt,cnt,c[N],d1[N],d2[N],s;
bool ins[N],v[N];
inline void add(int x,int y)
{
g[++tot].ver=y;
g[tot].next=head[x],head[x]=tot;
}
inline void addG(int x,int y)
{
G[++totG].ver=y;
G[totG].next=headG[x],headG[x]=totG;
}
inline void addG2(int x,int y)
{
G2[++totG2].ver=y;
G2[totG2].next=headG2[x],headG2[x]=totG2;
}
inline void tarjan(int x)
{
dfn[x]=low[x]=++cnt;
stack[++top]=x,ins[x]=1;
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(ins[y]) low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x]){
++idt;int y;
do{
y=stack[top--],ins[y]=0;
if(y==1) s=idt;
c[y]=idt,scc[idt]++;
}while(x!=y);
}
}
inline void spfa1()
{
memset(d1,0,sizeof(d1));
queue<int> q;
d1[s]=scc[s];q.push(s);
while(q.size()){
int x=q.front();q.pop();
v[x]=0;
for(int i=headG[x];i;i=G[i].next){
int y=G[i].ver;
if(d1[y]<d1[x]+scc[y]){
d1[y]=d1[x]+scc[y];
if(!v[y]) v[y]=1,q.push(y);
}
}
}
}
inline void spfa2()
{
memset(d2,0,sizeof(d2));
queue<int> q;
d2[s]=scc[s];q.push(s);
while(q.size()){
int x=q.front();q.pop();
v[x]=0;
for(int i=headG2[x];i;i=G2[i].next){
int y=G2[i].ver;
if(d2[y]<d2[x]+scc[y]){
d2[y]=d2[x]+scc[y];
if(!v[y]) v[y]=1,q.push(y);
}
}
}
}
int main()
{
//IN
n=read(),m=read();
for(int i=1;i<=m;++i){
int u,v;
u=read(),v=read();
add(u,v);
}
for(int i=1;i<=n;++i)
if(!dfn[i]) tarjan(i);
for(int x=1;x<=n;++x)
for(int i=head[x];i;i=g[i].next){
int y=g[i].ver;
if(c[x]==c[y]) continue;
addG(c[x],c[y]),addG2(c[y],c[x]);
}
int ans=scc[s];//注意有可能只有一个强连通分量,那答案就是他自己
spfa1();
spfa2();
for(int i=1;i<=idt;++i){
//这里利用到一个技巧,没有被SPFA标记的地方就是最长路径的终点,逆行边的起点
//这样就可以快速找出逆行边的位置并进行统计
//即逆行边是一条y->i的边,而利用反图恰恰可以快速找出所有这样的边
if(!v[i]&&d1[i]){
v[i]=1;
for(int j=headG2[i];j;j=G2[j].next){
int y=G2[j].ver;
if(!d2[y]) continue;//及时排除不可达情况
ans=max(ans,d1[i]+d2[y]-scc[s]);
}
}
}
cout<<ans<<endl;
return 0;
}

P3119 [USACO15JAN]草鉴定[SCC缩点+SPFA]的更多相关文章

  1. 洛谷 P3119 [USACO15JAN]草鉴定Grass Cownoisseur (SCC缩点,SPFA最长路,枚举反边)

    P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of hi ...

  2. 洛谷 P3119 [USACO15JAN]草鉴定Grass Cownoisseur 解题报告

    P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 约翰有\(n\)块草场,编号1到\(n\),这些草场由若干条单行道相连.奶牛贝西是美味牧草的鉴赏家,她想到达尽可 ...

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

    P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of hi ...

  4. [Luogu P3119] [USACO15JAN]草鉴定Grass Cownoisseur (缩点+图上DP)

    题面 传送门:https://www.luogu.org/problemnew/show/P3119 Solution 这题显然要先把缩点做了. 然后我们就可以考虑如何处理走反向边的问题. 像我这样的 ...

  5. P3119 [USACO15JAN]草鉴定Grass Cownoisseur

    题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-w ...

  6. 洛谷P3119 USACO15JAN 草鉴定

    题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-w ...

  7. luogu P3119 [USACO15JAN]草鉴定Grass Cownoisseur

    题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-w ...

  8. luogu3119/bzoj3887 草鉴定 (tarjan缩点+spfa)

    首先缩一波点,就变成了一个DAG,边权是出点的大小 那我们走到某个点的时候可能会有两种状态:已经走过反边或者没走过 于是就把一个点拆成两层(x和x+N),第二层的点表示我已经走过反边了,每层中的边和原 ...

  9. 洛谷—— P3119 [USACO15JAN]草鉴定Grass Cownoisseur || BZOJ——T 3887: [Usaco2015 Jan]Grass Cownoisseur

    http://www.lydsy.com/JudgeOnline/problem.php?id=3887|| https://www.luogu.org/problem/show?pid=3119 D ...

随机推荐

  1. 托马斯·贝叶斯 (Thomas Bayes)

    朴素贝叶斯   Day15,开始学习朴素贝叶斯,先了解一下贝爷,以示敬意. 托马斯·贝叶斯 (Thomas Bayes),英国神学家.数学家.数理统计学家和哲学家,1702年出生于英国伦敦,做过神甫: ...

  2. kindeditor——开源的HTML可视化编辑器

    官网:http://kindeditor.net/demo.php 主要操作文档:http://kindeditor.net/docs/option.html

  3. POJ 1941 The Sierpinski Fractal

    总时间限制: 1000ms 内存限制: 65536kB 描述 Consider a regular triangular area, divide it into four equal triangl ...

  4. #redis 安装命令

    redis 安装命令 cd /usr/local sudo wget http://download.redis.io/releases/redis-4.0.11.tar.gz sudo tar xf ...

  5. Python解释器安装与环境变量添加

    Python解释器安装与环境变量添加 Python解释器安装(3.6和2.7): www.python.org这个是python解释器的官网,一定要牢记. 鉴于市场上有两种python版本(2和3), ...

  6. const指针和指向常量的指针

    先看下面六种写法: . const int p; . const int *p; . int const* p; . int * const p; . const int * const p; . i ...

  7. Linux远程管理命令

    关机\重启 shutdown 选项 时间 参数 -r 重启 例子: shutdown 1分钟后关机 shutdown now 立刻关机 shutdown –r now 立即重启 shutdown 20 ...

  8. 使用PHP开发HR系统(1)

    本文通过笔者的实践,讲述如何以PHP+CI+Postgres构建一套人力资源管理系统. ======================================================== ...

  9. (三)pdf的构成之文件头综述

    引自:https://blog.csdn.net/steve_cui/article/details/81981943 一般情况下,文件头,即,PDF文件的第一行,它用来定义PDF的版本,从而确定该P ...

  10. 手贱删了一些不该删的东西,导致git pull每次都要输入账号密码...

    解决办法: 输入一次账号密码,然后git工程目录下执行  git config --global credential.helper store 就妥了.