P3119 [USACO15JAN]草鉴定[SCC缩点+SPFA]
题目描述
约翰有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]的更多相关文章
- 洛谷 P3119 [USACO15JAN]草鉴定Grass Cownoisseur (SCC缩点,SPFA最长路,枚举反边)
P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of hi ...
- 洛谷 P3119 [USACO15JAN]草鉴定Grass Cownoisseur 解题报告
P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 约翰有\(n\)块草场,编号1到\(n\),这些草场由若干条单行道相连.奶牛贝西是美味牧草的鉴赏家,她想到达尽可 ...
- 洛谷——P3119 [USACO15JAN]草鉴定Grass Cownoisseur
P3119 [USACO15JAN]草鉴定Grass Cownoisseur 题目描述 In an effort to better manage the grazing patterns of hi ...
- [Luogu P3119] [USACO15JAN]草鉴定Grass Cownoisseur (缩点+图上DP)
题面 传送门:https://www.luogu.org/problemnew/show/P3119 Solution 这题显然要先把缩点做了. 然后我们就可以考虑如何处理走反向边的问题. 像我这样的 ...
- P3119 [USACO15JAN]草鉴定Grass Cownoisseur
题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-w ...
- 洛谷P3119 USACO15JAN 草鉴定
题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-w ...
- luogu P3119 [USACO15JAN]草鉴定Grass Cownoisseur
题目描述 In an effort to better manage the grazing patterns of his cows, Farmer John has installed one-w ...
- luogu3119/bzoj3887 草鉴定 (tarjan缩点+spfa)
首先缩一波点,就变成了一个DAG,边权是出点的大小 那我们走到某个点的时候可能会有两种状态:已经走过反边或者没走过 于是就把一个点拆成两层(x和x+N),第二层的点表示我已经走过反边了,每层中的边和原 ...
- 洛谷—— 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 ...
随机推荐
- java8新特性(2)--接口的默认方法
1.默认方法的定义和作用 在Java8以前的版本中,由接口定义的方法是抽象的,不包括方法体.JDK8版本的发布改变了这一点,其中给接口添加了一个新的功能:默认方法.默认方法允许为接口方法定义默认实现. ...
- SpringBoot项目使用RedisTemplate设置序列化方式
前端时间新项目使用SpringBoot的RedisTemplate遇到一个问题,先简单描述一下问题:不同项目之间redis共用一个,但是我们新项目读不到老项目存储的缓存.新项目搭建的时候没有跟老项目使 ...
- Appium移动端测试--搭建测试环境
目录 文章目录如下 安装Android Studio及Android SDK 更改VDM默认存储路径 通过npm安装Appium Server 启动Appium GUI模式 Appium连接会话 Ap ...
- LeetCode第8场双周赛(Java)
这次我只做对一题. 原因是题目返回值类型有误,写的是 String[] ,实际上应该返回 List<String> . 好吧,只能自认倒霉.就当涨涨经验. 5068. 前后拼接 解题思路 ...
- kubernetes 实践二:kubectl命令使用
这里记录kubernetes学习和使用过程中的内容. CentOS7 k8s-1.13 flanneld-0.10 docker-18.06 etcd-3.3 kubectl用法概述 kubectl是 ...
- [LOJ2065] [SDOI2016]模式字符串
题目链接 洛谷:https://www.luogu.org/problemnew/show/P4075 LOJ:https://loj.ac/problem/2065 Solution 这种题看起来就 ...
- Kubernetes1.11.1 使用Nvidia显卡配置方法
一.安装 1.1.kubernetes硬件支持问题说明 Kubernetes目前主要在很小程度上支持CPU和内存的发现.Kubelet本身处理的设备非常少.Kubernetes对于硬件都使用都依赖于硬 ...
- NIO(2):Channel
Channel可以理解为铁轨,Buffer是铁轨上的火车.铁轨的两端连接这文件描述符或者说文件的缓冲区和程序运行时的内存.借助NIO是一种更加符合OS底层文件系统的调用方式,使用NIO可以用更小的开销 ...
- Matlab图形绘制
图形绘制 离散函数图形: 例:离散数据和离散函数可视化(离散数据作图方式) X1=[,,,,,,,,,,,,,,,,,,,]; Y1=[,,,,,,,,,,,,,,,,,,,]; figure() p ...
- 15天入门RT-Thread之第一天
今天开始学习jiezhi15天的RT-Thread入门系列课程 感谢RT-Thread提供的免费课程,终于可以系统入门RT-Thread ,感兴趣的同学可以关注RT-Thread官方公众号,获取最新的 ...