1. 程式人生 > >Luogu 3119 [USACO15JAN]草鑒定Grass Cownoisseur

Luogu 3119 [USACO15JAN]草鑒定Grass Cownoisseur

none 初始 復雜 open back 最長鏈 dfs clas lose

思路很亂,寫個博客理一理。

縮點 + dp。

首先發現把一個環上的邊反向是意義不大的,這樣子不但不好算,而且相當於浪費了一次反向的機會。反正一個強連通分量裏的點繞一遍都可以走到,所以我們縮點之後把一個強連通分量放在一起處理。

設$st$表示縮點之後$1$所在的點,設$f_{x}$表示從$st$走到$x$的最長鏈,$g_{x}$表示從$x$走到$st$的最長鏈,因為把一個$DAG$上的邊反向一下並不會走重復的點,那麽我們最後枚舉一下邊$(x, y)$,把它反向,這樣子$f_{x} + g_{y} - siz_{st}$就可以成為備選答案,更新$ans$即可。

註意到有可能整個圖強連通,所以$ans$應初始化為$siz_{st}$。

考慮一下$f$和$g$怎麽求,一種想法是$st$開始的最長路,我們可以在反圖和正圖上分別跑一遍$spfa$,這樣子可以通過,但是我並不清楚在$DAG$上$spfa$的表現是不是穩定的,另一種想法就是$dp$,直接記搜搞一搞,但是直接記搜是錯誤的,因為有一些點是不可能走到的,所以在$dp$之前要先$dfs$一遍標記出所有的合法點,然後再進行記搜即可。

時間復雜度$O(n)$。

放上寫得很醜很長還可能有鍋的代碼。

你谷的數據是真的水。

Code:

技術分享圖片
#include <cstdio>
#include <cstring>
#include <vector>
using
namespace std; const int N = 1e5 + 5; int n, m, tot = 0, head[N], dfsc = 0, dfn[N], low[N]; int scc = 0, f[N], g[N], inx[N], iny[N], top = 0, sta[N], bel[N], siz[N]; bool vis[N], ok[N]; vector <int> G1[N], G2[N]; struct Edge { int to, nxt; } e[N]; inline void add(int from, int
to) { e[++tot].to = to; e[tot].nxt = head[from]; head[from] = tot; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > 9 || ch < 0; ch = getchar()) if(ch == -) op = -1; for(; ch >= 0 && ch <= 9; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int min(int x, int y) { return x > y ? y : x; } void tarjan(int x) { low[x] = dfn[x] = ++dfsc; vis[x] = 1, sta[++top] = x; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(!dfn[y]) { tarjan(y); low[x] = min(low[x], low[y]); } else if(vis[y]) low[x] = min(low[x], dfn[y]); } if(low[x] == dfn[x]) { ++scc; for(; sta[top + 1] != x; --top) { vis[sta[top]] = 0; siz[scc]++; bel[sta[top]] = scc; } } } inline void chkMax(int &x, int y) { if(y > x) x = y; } void dfs1(int x) { ok[x] = 1, vis[x] = 1; for(unsigned int i = 0; i < G1[x].size(); i++) { int y = G1[x][i]; dfs1(y); } } int dp1(int x) { if(vis[x]) return f[x]; vis[x] = 1; int res = 0; for(unsigned int i = 0; i < G2[x].size(); i++) { int y = G2[x][i]; if(ok[y]) chkMax(res, dp1(y)); } f[x] = res + siz[x]; return f[x]; } void dfs2(int x) { ok[x] = 1, vis[x] = 1; for(unsigned int i = 0; i < G2[x].size(); i++) { int y = G2[x][i]; dfs2(y); } } int dp2(int x) { if(vis[x]) return g[x]; vis[x] = 1; int res = 0; for(unsigned int i = 0; i < G1[x].size(); i++) { int y = G1[x][i]; if(ok[y]) chkMax(res, dp2(y)); } g[x] = res + siz[x]; return g[x]; } int main() { // freopen("testdata.in", "r", stdin); read(n), read(m); for(int i = 1; i <= m; i++) { read(inx[i]), read(iny[i]); add(inx[i], iny[i]); } for(int i = 1; i <= n; i++) if(!dfn[i]) tarjan(i); for(int i = 1; i <= m; i++) { if(bel[inx[i]] == bel[iny[i]]) continue; G1[bel[inx[i]]].push_back(bel[iny[i]]); G2[bel[iny[i]]].push_back(bel[inx[i]]); } memset(vis, 0, sizeof(vis)); memset(ok, 0, sizeof(ok)); dfs1(bel[1]); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= scc; i++) { if(ok[i]) dp1(i); } memset(vis, 0, sizeof(vis)); memset(ok, 0, sizeof(ok)); dfs2(bel[1]); memset(vis, 0, sizeof(vis)); for(int i = 1; i <= scc; i++) { if(ok[i]) dp2(i); } /* for(int i = 1; i <= scc; i++) printf("%d ", g[i]); printf("\n"); for(int i = 1; i <= scc; i++) printf("%d ", f[i]); printf("\n"); */ int ans = siz[bel[1]]; for(int i = 1; i <= m; i++) { int u = bel[iny[i]], v = bel[inx[i]]; if(u == v) continue; if(f[u] && g[v]) chkMax(ans, f[u] + g[v] - siz[bel[1]]); } printf("%d\n", ans); return 0; }
View Code

Luogu 3119 [USACO15JAN]草鑒定Grass Cownoisseur