[bzoj3887][Usaco2015 Jan]Grass Cownoisseur_trajan_拓扑排序_拓扑序dp
[Usaco2015 Jan]Grass Cownoisseur
题目大意:给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1)
数据范围:$1\le n, m\le 10^5$。
题解:
先$tarjan$缩强连通分量,因为每一个$SCC$只要能到一个点就能到整个$SCC$。
接下来我们发现,我们操作的边的两个端点会满足如下性质:
这条有向边的起点可以到$1$号点所在$SCC$。
这条有向边的重点可以被$1$号点所在$SCC$到达。
故此,我们再缩完点之后,先对原图弄一遍拓扑序$DP$,求出$1$号点所在$SCC$到每个点的最长路。
再建反边重新跑拓扑序$DP$,求出每个点到$1$号点所在$SCC$的最长路。
暴力枚举边更新即可。
代码:
#include <bits/stdc++.h> #define N 100010 using namespace std; int dep[N], low[N], st[N], top, cnt, blg[N], sz[N], f1[N], f2[N], d1[N], d2[N]; int Number; bool ins[N]; struct Node {
int x, y;
}e[N]; struct Edge {
int head[N], to[N << 1], nxt[N << 1], tot;
inline void add(int x, int y) {
to[ ++ tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
}G1,G2,G3; char *p1, *p2, buf[100000]; #define nc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1 ++ ) int rd() {
int x = 0, f = 1;
char c = nc();
while (c < 48) {
if (c == '-')
f = -1;
c = nc();
}
while (c > 47) {
x = (((x << 2) + x) << 1) + (c ^ 48), c = nc();
}
return x * f;
} void tarjan(int p) {
st[ ++ top] = p;
ins[p] = true;
low[p] = dep[p] = ++cnt;
for (int i = G1.head[p]; i; i = G1.nxt[i]) {
if (!dep[G1.to[i]])
tarjan(G1.to[i]), low[p] = min(low[p], low[G1.to[i]]);
else if (ins[G1.to[i]])
low[p] = min(low[p], dep[G1.to[i]]);
}
if (dep[p] == low[p]) {
int t;
Number ++ ;
do {
t = st[top -- ];
ins[t] = false;
blg[t] = Number;
sz[Number] ++ ;
} while(t != p);
}
} queue<int> q; void dp1() {
while (!q.empty()) {
q.pop();
}
memset(f1, 0xef, sizeof f1);
for (int i = 1; i <= Number; i ++ ) {
if (!d1[i]) {
q.push(i);
}
}
f1[blg[1]] = 0;
while (!q.empty()) {
int x = q.front();
q.pop();
f1[x] += sz[x];
for (int i = G2.head[x]; i; i = G2.nxt[i]) {
f1[G2.to[i]] = max(f1[G2.to[i]], f1[x]);
d1[G2.to[i]] -- ;
if (!d1[G2.to[i]]) {
q.push(G2.to[i]);
}
}
}
} void dp2() {
while (!q.empty()) {
q.pop();
}
for (int i = 1; i <= Number; i ++ ) {
if (!d2[i]) {
q.push(i);
}
}
memset(f2, 0xef, sizeof f2);
f2[blg[1]] = 0;
while (!q.empty()) {
int x = q.front();
q.pop();
f2[x] += sz[x];
for (int i = G3.head[x]; i; i = G3.nxt[i]) {
f2[G3.to[i]] = max(f2[G3.to[i]], f2[x]);
d2[G3.to[i]] -- ;
if (!d2[G3.to[i]]) {
q.push(G3.to[i]);
}
}
}
} int main() {
int n = rd(), m = rd();
if (!n)
puts("1"), exit(0);
for (int i = 1; i <= m; i ++ ) {
e[i].x = rd(), e[i].y = rd();
G1.add(e[i].x, e[i].y);
}
for (int i = 1; i <= n; i ++ ) {
if (!dep[i]) {
tarjan(i);
}
}
// for (int i = 1; i <= n; i ++ ) {
// printf("%d ",blg[i]);
// }
// puts("");
for (int i = 1; i <= m; i ++ ) {
e[i].x = blg[e[i].x];
e[i].y = blg[e[i].y];
if (e[i].x != e[i].y) {
// printf("%d %d\n", e[i].x, e[i].y);
G2.add(e[i].x, e[i].y);
d1[e[i].y] ++ ;
G3.add(e[i].y, e[i].x);
d2[e[i].x] ++ ;
}
}
dp1();
dp2();
// for (int i = 1; i <= Number; i ++ ) {
// printf("%d %d\n", f1[i], f2[i]);
// }
int ans = sz[blg[1]];
for (int i = 1; i <= m; i ++ ) {
if (e[i].x != e[i].y) {
ans = max(ans, f1[e[i].y] + f2[e[i].x] - sz[blg[1]]);
}
}
cout << ans << endl ;
return 0;
}
小结:比较好想的一道题,需要注意的是两边拓扑序$dp$需要清队列。
[bzoj3887][Usaco2015 Jan]Grass Cownoisseur_trajan_拓扑排序_拓扑序dp的更多相关文章
- BZOJ3887 [Usaco2015 Jan] Grass Cownoisseur 【tarjan】【DP】*
BZOJ3887 [Usaco2015 Jan] Grass Cownoisseur Description In an effort to better manage the grazing pat ...
- bzoj3887: [Usaco2015 Jan]Grass Cownoisseur
题意: 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1) =>有向图我们 ...
- BZOJ3887 [Usaco2015 Jan]Grass Cownoisseur[缩点]
首先看得出缩点的套路.跑出DAG之后,考虑怎么用逆行条件.首先可以不用,这样只能待原地不动.用的话,考虑在DAG上向后走,必须得逆行到1号点缩点后所在点的前面,才能再走回去. 于是统计从1号点缩点所在 ...
- BZOJ_3887_[Usaco2015 Jan]Grass Cownoisseur_强连通分量+拓扑排序+DP
BZOJ_3887_[Usaco2015 Jan]Grass Cownoisseur_强连通分量+拓扑排序+DP Description In an effort to better manage t ...
- [补档][Usaco2015 Jan]Grass Cownoisseur
[Usaco2015 Jan]Grass Cownoisseur 题目 给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过? (一个点在路 ...
- BZOJ 3887/Luogu P3119: [Usaco2015 Jan]Grass Cownoisseur (强连通分量+最长路)
分层建图,反向边建在两层之间,两层内部分别建正向边,tarjan缩点后,拓扑排序求一次1所在强连通分量和1+n所在强联通分量的最长路(长度定义为路径上的强联通分量内部点数和).然后由于1所在强连通分量 ...
- Codeforces 919D Substring ( 拓扑排序 && DAG上的DP )
题意 : 给出含有 N 个点 M 条边的图(可能不连通或者包含环),每个点都标有一个小写字母编号,然后问你有没有一条路径使得路径上重复字母个数最多的次数是多少次,例如图上有条路径的顶点标号顺序是 a ...
- 洛谷—— 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 ...
- [Usaco2015 Jan]Grass Cownoisseur Tarjan缩点+SPFA
考试的时候忘了缩点,人为dfs模拟缩点,没想到竟然跑了30分,RB爆发... 边是可以重复走的,所以在同一个强连通分量里,无论从那个点进入从哪个点出,所有的点一定能被一条路走到. 要使用缩点. 然后我 ...
随机推荐
- Verilog从文件读数据
reg start;reg [17:0] counter;always @(posedge i_clk)//置rst.startbegin //产生读数据地址 if(counter==171519|| ...
- BZOJ 2440 [中山市选2011]完全平方数 二分+容斥
直接筛$\mu$?+爆算?再不行筛素数再筛个数?但不就是$\mu^2$的前缀和吗? 放...怕不是数论白学了$qwq$ 思路:二分+容斥 提交:两次(康了题解) 题解: 首先答案满足二分性质(递增), ...
- cookie与session的区别?
一.cookie机制和session机制的区别 具体来说cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案.同时我们也看到,由于才服务器端保持状态的方 ...
- Ubuntu16.04下安装多版本cuda和cudnn
Ubuntu16.04下安装多版本cuda和cudnn 原文 https://blog.csdn.net/tunhuzhuang1836/article/details/79545625 前言 因为之 ...
- linux 搭建 jenkins 前端自动构建时,老是提示 sh: vue-cli-service: command not found
如题. 在 shell 里面执行 env 发现一个 NODE_ENV=production 的环境变量,是这个东西的原因. 通过 unset NODE_ENV 命令去掉这个环境变量就可以了 ...
- 腾讯域名防封 微信/QQ域名检测,域名防封的原理
微信屏蔽网页的依据是什么?明面上的原因是网页内容有诱导.诈骗等不和谐的内容时候,被用户举报就会封闭.实际上这只是表面现象,因为我们能明确的感受到不同的阶段,同样的内容,被封杀的频率差别很大的,也就是说 ...
- MIME协议(四) -- MIME消息的头字段
MIME消息的头字段 4.1 Content-Type 对于表示某个具体资源的MIME消息,它的消息头中需要指定资源的数据类型:对于MIME组合消息,它的消息头中需要指定组合关系.具体资源的数据类型 ...
- Druid数据源监控配置
在web.xml中添加如下代码 <!-- druid监控 --> <servlet> <servlet-name>DruidStatView</servlet ...
- POJ 3083 -- Children of the Candy Corn(DFS+BFS)TLE
POJ 3083 -- Children of the Candy Corn(DFS+BFS) 题意: 给定一个迷宫,S是起点,E是终点,#是墙不可走,.可以走 1)先输出左转优先时,从S到E的步数 ...
- 【JDBC】使用Spring提供的JDBCTemplate通过Statement向MySql数据库插入千万条数据,耗时4m55s,使用insert语句批量插入方式二
这回依然是使用 insert批量插入这种方式 insert into emp(name,age,cdate) values ('A' , 20, '2019-10-13 00:00:00'), ('B ...