「6月雅礼集训 2017 Day7」电报

Posted 逢山开路 遇水架桥

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了「6月雅礼集训 2017 Day7」电报相关的知识,希望对你有一定的参考价值。

【题目大意】

有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」电报的主要内容,如果未能解决你的问题,请参考以下文章

「6月雅礼集训 2017 Day11」tree

「6月雅礼集训 2017 Day10」quote

「6月雅礼集训 2017 Day11」jump

「6月雅礼集训 2017 Day8」gcd

「6月雅礼集训 2017 Day5」吃干饭

「6月雅礼集训 2017 Day8」infection