「BZOJ 1791」「IOI 2008」Island「基環樹」
阿新 • • 發佈:2019-02-09
題解 隊列 假設 emp tro return sca def 結點 就行,類似滑動窗口。
題意
求基環樹森林所有基環樹的直徑之和
題解
考慮的一個基環樹的直徑,只會有兩種情況,第一種是某個環上結點子樹的直徑,第二種是從兩個環上結點子樹內的最深路徑,加上環上這兩個結點之間的較長路徑。
那就找環,然後環上每個結點做樹形\(dp\)。然後把環斷成長度為\(2n\)的鏈,記錄環上的前綴和\(sum\)。假設結點\(u\)子樹內最深路徑為\(dep[u]\),那麽就是求\(max(sum[i] - sum[j] + dep[i] + dep[j]),j < i\)。這個就轉換成\(max(sum[i] + dep[i] + dep[j] - sum[j])\),用單調隊列維護最大的\(dep[j] - sum[j]\)
#include <algorithm> #include <cstring> #include <cstdio> #include <vector> #include <queue> using namespace std; typedef long long ll; const int N = 1e6 + 5; struct Edge { int v, nxt, w; } e[N << 1]; int hd[N], edge_num; void link(int u, int v, int w) { e[edge_num] = (Edge) {v, hd[u], w}; hd[u] = edge_num ++; } int n, loop[N], cnt, idx, dfn[N]; int fa[N], faw[N], loopw[N]; ll dep1[N], dep2[N], tree_d; ll sum[N << 1], dep[N << 1]; bool onloop[N]; deque<int> q; void dfs(int u, int cur = -1) { dfn[u] = ++ idx; for(int i = hd[u]; ~ i; i = e[i].nxt) { if(cur == i) continue ; int v = e[i].v; if(dfn[v]) { if(dfn[v] > dfn[u] || cnt) continue ; cnt ++; loop[cnt] = u; loopw[cnt] = e[i].w; for(int j = u; j != v; j = fa[j]) { cnt ++; loop[cnt] = fa[j]; loopw[cnt] = faw[j]; } } else fa[v] = u, faw[v] = e[i].w, dfs(v, i ^ 1); } } void dp(int u, int f = 0) { dep1[u] = dep2[u] = 0; for(int i = hd[u]; ~ i; i = e[i].nxt) { int v = e[i].v; if(v == f || onloop[v]) continue ; dp(v, u); ll dis = e[i].w + dep1[v]; if(dis > dep1[u]) { dep2[u] = dep1[u]; dep1[u] = dis; } else if(dis > dep2[u]) { dep2[u] = dis; } } tree_d = max(tree_d, dep1[u] + dep2[u]); } int main() { scanf("%d", &n); fill(hd + 1, hd + n + 1, -1); for(int i = 1, v, w; i <= n; i ++) { scanf("%d%d", &v, &w); link(v, i, w); link(i, v, w); } ll ans = 0, d; for(int i = 1; i <= n; i ++) if(!dfn[i]) { cnt = 0; dfs(i); for(int j = 1; j <= cnt; j ++) onloop[loop[j]] = 1; d = 0; for(int j = 1; j <= cnt; j ++) { tree_d = 0; dp(loop[j]); dep[j] = dep[j + cnt] = dep1[loop[j]]; d = max(d, tree_d); } for(int j = 1; j <= cnt << 1; j ++) sum[j] = sum[j - 1] + loopw[j <= cnt ? j : j - cnt]; q.clear(); for(int j = 1; j <= cnt << 1; j ++) { //sum_j + dep_j + dep_i - sum_i for(; !q.empty() && q.front() + cnt - 1 < j; q.pop_front()) ; ll c = dep[j] - sum[j]; if(!q.empty()) d = max(d, sum[j] + dep[j] + dep[q.front()] - sum[q.front()]); for(; !q.empty() && dep[q.back()] - sum[q.back()] <= c; q.pop_back()) ; q.push_back(j); } ans += d; } printf("%lld\n", ans); return 0; }
什麽,\(\text{BZOJ}\)上\(10^6\)會爆棧?那只能手工棧了。。
「BZOJ 1791」「IOI 2008」Island「基環樹」