• [bzoj3887][Usaco2015 Jan]Grass Cownoisseur_trajan_拓扑排序_拓扑序dp


    [Usaco2015 Jan]Grass Cownoisseur

    目大给一个有向图,然后选一条路径起点终点都为1的路径出来,有一次机会可以沿某条边逆方向走,问最多有多少个点可以被经过?(一个点在路径中无论出现多少正整数次对答案的贡献均为1)

    数据范围:$1le n, mle 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
    ", 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
    ", 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$需要清队列。

  • 相关阅读:
    java从Excle中读取数据集
    使用iText5实现Java打印PDF文件完整版
    layer.photos动态加载图片及静态加载图片
    html 设置th时 width无效 解决办法
    前端时间格式转换,js时间戳转时间(年-月-日 时:分:秒)
    JS选择日期控件,当前日期以后的日期不能选择
    Navicat安装和破解
    Git下载安装及Idea配置教程(亲测使用)
    maven的安装及配置
    测试面试常见问题
  • 原文地址:https://www.cnblogs.com/ShuraK/p/11255775.html
Copyright © 2020-2023  润新知