这大概是我写过的除了树剖以外最长的代码了吧

首先看到有向图和重复经过等敏感词应该能想到先tarjan后缩点了吧

首先有一个naive的想法,既然我们要求只能走一次返回原点,那我们就正着反着建两遍图,分别处理出1到其他点的所能经过的最多点数和其他点到1经过的最大点数,之后找到那些和1有正边或反边相连的点,之后逆行这一条边,取一个max就好了

于是洋洋洒洒写了100多行发现连样例都过不了

本着交一交试试看的心态交了上去,发现还能过四个点

于是就去手画了一遍样例,发现自己真的是非常naive

样例是这个样子的

尽管很丑,但就勉强看看吧

我们发现按照刚才那个思路我们显然是要挂的

因为按照那样跑出来的结果是4,但这里的最佳方案应该是从1到那个缩好的点再到5,之后逆行一次走到3,之后走回1

这样我们如果只考虑能到到1的或只考虑能被1到达的,显然是不行的

这样不行怎么办,我们放到一起考虑就好了

如果一个能被1到达的点(比如说样例里的5)有一条边(当然这是一条反边)能到达一个能到达1的点,我们就可以把这两种情况一起考虑

同理一个能到达1的点有一条边(自然这是一条正边)和一个能被1到达的点相连,这两种情况也可以一起考虑

所以就是代码了

#include<iostream>
#include<cstring>
#include<cstdio>
#include<bitset>
#define re register
#define maxn 100001
using namespace std;
struct node
{
int v,nxt;
}e[maxn],e1[maxn],e2[maxn];
int head[maxn],head1[maxn],head2[maxn];
int dfn[maxn],low[maxn],st[maxn],belong[maxn],dp1[maxn],d[maxn],dp2[maxn];
int f1[maxn],f2[maxn];
int q[maxn],r[maxn],c[maxn];
bitset<maxn> f;//闲的没事干开了bitset
int n,m,top,k,p,num,num1,num2,mid;
inline void add_edge(int x,int y)
{
e[++num].v=y;
e[num].nxt=head[x];
head[x]=num;
}//原图
inline void add_edge_1(int x,int y)
{
e1[++num1].v=y;
e1[num1].nxt=head1[x];
head1[x]=num1;
}//缩点后新图建正边
inline void add_edge_2(int x,int y)
{
e2[++num2].v=y;
e2[num2].nxt=head2[x];
head2[x]=num2;
}//缩点后新图建反边
inline int read()
{
char c=getchar();
int x=0;
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9')
x=(x<<3)+(x<<1)+c-48,c=getchar();
return x;
}
void tarjan(int x)
{
dfn[x]=low[x]=++k;
f[x]=1;
st[++top]=x;
for(re int i=head[x];i;i=e[i].nxt)
if(!dfn[e[i].v]) tarjan(e[i].v),low[x]=min(low[x],low[e[i].v]);
else if(f[e[i].v]) low[x]=min(low[x],dfn[e[i].v]);
if(dfn[x]==low[x])
{
p++;
do
{
mid=st[top--];
f[mid]=0;
d[p]++;//记录新点点权
belong[mid]=p;//记录好每一个点属于哪一个缩完点后的新点
}while(x!=mid);
}
}
int main()
{
n=read();
m=read();
int x,y;
for(re int i=1;i<=m;i++)
{
x=read();
y=read();
add_edge(x,y);
}
for(re int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);//缩点!
for(re int i=1;i<=n;i++)
for(re int j=head[i];j;j=e[j].nxt)
if(belong[i]!=belong[e[j].v])
{
r[belong[e[j].v]]++,add_edge_1(belong[i],belong[e[j].v]);//建正图
c[belong[i]]++,add_edge_2(belong[e[j].v],belong[i]);//建反图
}
f[belong[1]]=1;//我们开一个标记数组,标记那些点可以被1到达,拓扑排序的时候只有这些点才进行动规,其余的点只做删边操作
dp1[belong[1]]=d[belong[1]];
int tot=0;
for(re int i=1;i<=p;i++)
if(!r[i]) q[++tot]=i;
for(re int i=1;i<=tot;i++)
{
for(re int j=head1[q[i]];j;j=e1[j].nxt)
{
r[e1[j].v]--;
if(f[q[i]])
{
f[e1[j].v]=1;
dp1[e1[j].v]=max(dp1[e1[j].v],dp1[q[i]]+d[e1[j].v]);
//dp1[i]表示从1到i形成的最大点权
}
if(!r[e1[j].v]) q[++tot]=e1[j].v;
}
}
int ans=0;
for(re int i=head1[belong[1]];i;i=e1[i].nxt)
ans=max(ans,dp1[e1[i].v]);
for(re int i=1;i<=p;i++)
f1[i]=f[i];//记录那些点可以被1到达
tot=0;
memset(q,0,sizeof(q));
f.reset();//bitset清零
f[belong[1]]=1;//标记同理,表示这个点可以到达1
dp2[belong[1]]=d[belong[1]];
for(re int i=1;i<=p;i++)
if(!c[i]) q[++tot]=i;
for(re int i=1;i<=tot;i++)
{
for(re int j=head2[q[i]];j;j=e2[j].nxt)
{
c[e2[j].v]--;
if(f[q[i]])
{
f[e2[j].v]=1;
dp2[e2[j].v]=max(dp2[e2[j].v],dp2[q[i]]+d[e2[j].v]);
//dp2[i]表示i这个点到1形成的最大点权
}
if(!c[e2[j].v]) q[++tot]=e2[j].v;
}
}
for(re int i=head2[belong[1]];i;i=e2[i].nxt)
ans=max(ans,dp2[e2[i].v]);
for(re int i=1;i<=p;i++)
f2[i]=f[i];//记录那些点可以到达1
for(re int i=1;i<=p;i++)
if(f1[i])//这个点可以被1到达
{
for(re int j=head2[i];j;j=e2[j].nxt)//逆行一次
if(f2[e2[j].v]) ans=max(ans,dp1[i]+dp2[e2[j].v]-d[belong[1]]);//由于两次dp都把1所在的强联通分量的点权算了进去,所以要减去一个1所在的强联通分量的点权
}
for(re int i=1;i<=p;i++)
if(f2[i])
{
for(re int j=head1[i];j;j=e1[j].nxt)
if(f1[e1[j].v]) ans=max(ans,dp2[i]+dp1[e1[j].v]-d[belong[1]]);
}//同理
ans=max(d[belong[1]],ans);//如果一谁都到达不了,谁也到不了,那么答案就是1的点权了
printf("%d",ans);
return 0;
}

【[USACO15JAN]草鉴定Grass Cownoisseur】的更多相关文章

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

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

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

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

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

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

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

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

  5. 【洛谷P3119】[USACO15JAN]草鉴定Grass Cownoisseur

    草鉴定Grass Cownoisseur 题目链接 约翰有n块草场,编号1到n,这些草场由若干条单行道相连.奶牛贝西是美味牧草的鉴赏家,她想到达尽可能多的草场去品尝牧草. 贝西总是从1号草场出发,最后 ...

  6. P3119 [USACO15JAN]草鉴定Grass Cownoisseur

    题目描述 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. 洛谷—— 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 ...

  9. 洛谷3119 [USACO15JAN]草鉴定Grass Cownoisseur

    原题链接 显然一个强连通分量里所有草场都可以走到,所以先用\(tarjan\)找强连通并缩点. 对于缩点后的\(DAG\),先复制一张新图出来,然后对于原图中的每条边的终点向新图中该边对应的那条边的起 ...

  10. Luogu 3119 [USACO15JAN]草鉴定Grass Cownoisseur

    思路很乱,写个博客理一理. 缩点 + dp. 首先发现把一个环上的边反向是意义不大的,这样子不但不好算,而且相当于浪费了一次反向的机会.反正一个强连通分量里的点绕一遍都可以走到,所以我们缩点之后把一个 ...

随机推荐

  1. nodejs封装mssql

    对mssql操作Sqlserver数据库的基本封装: 记录一下: /** * Created by chaozhou on 2015/9/18. */ var mssql = require('mss ...

  2. Vue指令(四)--v-model

    1.v-model的使用场景 1.v-model的使用,用于表单控件的数据绑定 2.v-model与value共同使用,实现选项框的选中事件,两者相同时,选中 3.v-model 与v-bind:va ...

  3. 前端小结(4)---- 页面加载loding....

    /*正在加载*/ function showLoading(elem) { var html = '<div class="loading"><div id=&q ...

  4. jxls实现基于excel模板的报表

    此文章是基于 搭建Jquery+SpringMVC+Spring+Hibernate+MySQL平台 一. jar包介绍 1. commons-collections-3.2.jar 2. commo ...

  5. C Primer Plus note4

    warning: implicit declaration of function 'pirntf' [-Wimplicit-function-declaration]| 这种警告是因为使用了 没有声 ...

  6. 深入浅出ConcurrentHashMap1.8

    转载:https://www.jianshu.com/p/c0642afe03e0 好文 关于文章中的疑问:为什么要构造一个反序链表,放在nextTable的i+n的位置上呢,在<深入分析Con ...

  7. 使用maven搭建Spring MVC

    在maven项目中搭建SpringMvc 1.pom文件 <span style="white-space:pre"> </span><propert ...

  8. 原生JS的轮播图

    学习前端也有一小段时间了,当初在学习javascript的时候,练手的一个轮播图实例,轮播图也是挺常见的了. 着是通过获取图片偏移量实现的.也实现了无缝切换.还有一点问题就是没有加上图片切换的时候的延 ...

  9. Python入门-内置函数一

    什么是内置函数?就是python给你提供的拿来直接用的函数,比如print,input等等,截止到python版本3.6.2 python一共提供了68个内置函数,他们就是python直接提供给我们的 ...

  10. html发展史简介(摘抄)

    1993年,IETF,Internet工程任务组(Internet Engineering Task Force)的简写.IETF又叫互联网工程任务组,成立于1985年底,是全球互联网最具权威的技术标 ...