题目描述

约翰有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. 【C/C++开发】C++编译指令#pragma pack的配对使用

    C++编译指令#pragma pack的配对使用 #pragma pack可以用来指定C++数据结构的成员变量的内存对齐数值(可选值为1,2,4,8,16). 本文主要是强调在你的头文件中使用pack ...

  2. K8S+GitLab+.net core-自动化分布式部署-1

    K8S+GitLab-自动化分布式部署ASP.NET Core(一) 部署环境 一.部署流程介绍 开发人员通过Git上传asp.net core 项目到Gilab,并编写好.gitlab-ci.yml ...

  3. Dubbo服务器与普通服务器的区别

    Dubbo是一个阿里巴巴开源出来的一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案. 1.什么是分布式服务框架 分布式有两个特点,分别是内聚性和透明性(比如 ...

  4. java8新特性1--Lambda表达式

    一.Lambda表达式是什么? Lambda表达式有两个特点 一是匿名函数,二是可传递. 匿名函数的应用场景是 通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用Lambda表达式.lam ...

  5. 魔术方法之__call、__callStatic

    1.__call() 作用,当调用不存在的方法时,会调用该方法.实际应用,当程序调用不存在的方法时,意外导致程序终止. .或者当你调用了受保护的或者是私人的方法时,也会自动调用__call方法 结果: ...

  6. 向php数组函数array_colum()传入奇怪的数组

    <?php // 向php数组函数array_colum()传入奇怪的数组 // array_colum()函数 返回行列数组的其中一列,可以用其他列的键充当键 $arr = [ [ 1, 2, ...

  7. flask框架(六)——闪现(get_flashed_message)、请求扩展、中间件(了解)

    message -设置:flash('aaa') -取值:get_flashed_message() -假设在a页面操作出错,跳转到b页面,在b页面显示a页面的错误信息 1 如果要用flash就必须设 ...

  8. golang 实现定时任务

    在实际开发过程中,我们有时候需要编写一些定时任务.当然我们可以使用crontab命令实现我们的需求.但是这种方法不满足一些定制化场景,同时会依赖具体的操作系统环境. 定时任务 在golang中我们可以 ...

  9. ColorMatrixFilter色彩矩阵滤镜

    ColorMatrixFilter色彩矩阵滤镜: /** * * *----------------------------------------* * | *** ColorMatrixFilte ...

  10. power shell命令添加SharePoint用户组与用户(用户为域用户)

    查看SharePoint用户组 Get-PnPGroup 查看某一用户组 Get-PnPGroup -Identity "用户组名" 查看某一用户组下的所有成员 Get-PnPGr ...