1. 程式人生 > >「6月雅禮集訓 2017 Day7」電報

「6月雅禮集訓 2017 Day7」電報

連通塊 print 要花 style view 最小 pre mes freopen

【題目大意】

有n個島嶼,第i個島嶼有有向發射站到第$p_i$個島嶼,改變到任意其他島嶼需要花費$c_i$的代價,求使得所有島嶼直接或間接聯通的最小代價。

$1 \leq n \leq 10^5, 1 \leq p_i,c_i \leq 10^9$

【題解】

顯然最後是個大環,特判原來就是大環的情況。

考慮每個連通塊最多保留多少。

樹的答案可以直接dp做出來。

環的答案,根據樹的答案dp出來。

h[x][0/1]表示當前做到環上第i個點,環是否被切斷了,的最大保留價值。

因為環必須被切斷一次。所以最後返回h[r.size()][1]即可。

為了方便代碼寫的是從後向前(不會涉及到初值特判問題)

懶得寫tarjan,寫的是和昨天”學外語“一樣的找環方法。。

技術分享
# include <vector>
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;

const int N = 1e5 + 10, M = 2e5 + 10;
const ll inf = 1e18;

vector
<int> ring[N]; int rn = 0; int n, p[M], c[M]; int head[N], nxt[M], to[M], tot = 0, deg[N]; inline void add(int u, int v) { // cout << u << "-->" << v << endl; ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v; } inline void adde(int u, int v) { add(u, v), add(v, u); }
namespace SOLVE_RINGS { struct us { int fa[N], n; inline void set(int _n) { n = _n; for (int i=1; i<=n; ++i) fa[i] = i; } inline int getf(int x) { return fa[x] == x ? x : fa[x] = getf(fa[x]); } inline void un(int fu, int fv) { fa[fu] = fv; } }U; bool inrings[N]; int c[N], cn = 0; bool vis[N]; inline void dfs_rings(int x) { if(vis[x]) { ++rn; for (int i=cn; i; --i) ring[rn].push_back(c[i]); return ; } c[++cn] = x; vis[x] = 1; for (int i=head[x]; i; i=nxt[i]) ++deg[x], dfs_rings(to[i]); --cn; } inline void find_rings() { U.set(n); for (int i=1; i<=n; ++i) { int fu = U.getf(p[i]), fv = U.getf(i); if(fu == fv) inrings[i] = 1; else U.un(fu, fv); } for (int i=1; i<=n; ++i) { if(!inrings[i]) continue; cn = 0; dfs_rings(i); } } inline void debug_rings() { for (int i=1; i<=rn; ++i) { printf("num = %d\n ", i); for (int j=0; j<ring[i].size(); ++j) cout << ring[i][j] << ; cout << endl; } } inline void clear_rings() { for (int i=1; i<=rn; ++i) ring[i].clear(); for (int i=1; i<=n; ++i) inrings[i] = vis[i] = deg[i] = 0; rn = 0; } } ll f[N], g[N], h[N][2]; inline void dfs_trees(int x, int fa) { ll mx = 0; for (int i=head[x]; i; i=nxt[i]) { if(to[i] == fa) continue; dfs_trees(to[i], x); mx = max(mx, (ll)c[to[i]]); f[x] += f[to[i]]; } g[x] = f[x]; f[x] += mx; } inline ll solve(vector<int> r) { ll mx = 0, s = 0, res = 0; for (int i=0; i<r.size(); ++i) { int pr = r[(i - 1 + r.size()) % r.size()], nx = r[(i + 1) % r.size()]; dfs_trees(r[i], pr); } h[r.size()][1] = -inf; h[r.size()][0] = 0; for (int i=r.size() - 1; ~i; --i) { int pr = r[(i - 1 + r.size()) % r.size()]; h[i][1] = h[i+1][1] + max(g[r[i]] + c[pr], f[r[i]]); h[i][1] = max(h[i][1], h[i+1][0] + f[r[i]]); h[i][0] = h[i+1][0] + g[r[i]] + c[pr]; } return h[0][1]; } int main() { // freopen("telegraph.in", "r", stdin); // freopen("telegraph.out", "w", stdout); cin >> n; for (int i=1; i<=n; ++i) { scanf("%d%d", p+i, c+i); add(p[i], i); } SOLVE_RINGS :: find_rings(); if(rn == 1 && ring[rn].size() == n) { puts("0"); return 0; } // SOLVE_RINGS :: debug_rings(); ll ans = 0, sum = 0; for (int i=1; i<=rn; ++i) ans += solve(ring[i]); for (int i=1; i<=n; ++i) sum += c[i]; cout << sum - ans; return 0; }
View Code

「6月雅禮集訓 2017 Day7」電報