CF 294E - Shaass the Great 树形DP 推式子

题意:

给你一颗带权无向树,你可以改变一条边的位置,使得改变后任意两点仍然可达,问改变后任意两点路径的和的最小值是多少?

题解:

随便推一推就可以了,思路很简单,只是推的时候一堆bug。

枚举改变的边,然后删掉这条边,会构成两个联通块,找到每个联通块中与其他点距离和最小的点,然后那条边连这两个点即可。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#include <bitset>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <cmath>
#ifdef LOCAL
#define debug(x) cout<<#x<<" = "<<(x)<<endl;
#else
#define debug(x) 1;
#endif

#define chmax(x,y) x=max(x,y)
#define chmin(x,y) x=min(x,y)
#define lson id<<1,l,mid
#define rson id<<1|1,mid+1,r
#define lowbit(x) x&-x
#define mp make_pair
#define pb push_back
#define fir first
#define sec second
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, int> pii;

const ll MOD = 1e9 + 7;
const double eps = 1e-10;
const int INF = 0x3f3f3f3f;
const ll INFLL = 0x3f3f3f3f3f3f3f3fll;
const int MAXN = 5e3 + 5;

vector<pii> G[MAXN];
ll dis[MAXN][MAXN];
struct nodes {
    int id;
    ll cost;
    bool operator < (const nodes & x) const {
        return cost > x.cost;
    }
};
int n;

void dijstral (int st) {
    priority_queue <nodes> q;
    q.push ( (nodes) {st, 0});
    memset (dis[st], 0x3f, sizeof (dis[st]) );
    dis[st][st] = 0;
    while (!q.empty() ) {
        nodes now = q.top();
        q.pop();
        for (auto i : G[now.id]) {
            if (dis[st][i.first] > now.cost + i.second) {
                q.push ( (nodes) {i.first, now.cost + i.second});
                dis[st][i.first] = now.cost + i.second;
            }
        }
    }
}

ll d1[MAXN], d2[MAXN];
int siz[MAXN];
int st[MAXN], en[MAXN];
int dfs_clock;

void dfs1 (int now, int par) {
    siz[now] = 1;
    st[now] = ++dfs_clock;
    for (auto i : G[now]) {
        if (i.first == par) continue;
        dfs1 (i.first, now);
        d1[now] += d1[i.first] + (ll) i.second * siz[i.first];
        siz[now] += siz[i.first];
    }
    en[now] = dfs_clock;
}

void dfs2 (int now, int par) {
    for (auto i : G[now]) {
        if (i.first == par) continue;
        d2[i.first] = d2[now] - (ll) i.second * siz[i.first] + (ll) i.second * (n - siz[i.first]);
        dfs2 (i.first, now);
    }

}

ll ans, tot;

ll d3[MAXN];

ll cal (int id, ll w, int par) {
    ll tmp = 0;
    ll minn = INFLL, minn2 = INFLL;
    for (int i = 1; i <= n; i++) {
        if (st[id] > st[i] || en[id] < st[i]) {
            d3[i] = d2[i] - dis[i][id] * siz[id] - d1[id];
            minn = min (minn, d3[i]);
        } else {
            d3[i] = d2[i] - (d2[id] - d1[id] + dis[i][id] * (n - siz[id]) );
            minn2 = min(minn2, d3[i]);
        }
        tmp += d3[i];
    }
    tmp += 2 * minn * siz[id] + w * (n - siz[id]) * siz[id] * 2 + 2 * minn2 * (n - siz[id]);
    return tmp;
}

void dfs3 (int now, int par) {
    for (auto i : G[now]) {
        if (i.first == par) continue;
        ans = min(ans, cal(i.first, i.sec, now));
        dfs3(i.first, now);
    }
}

int main() {
#ifdef LOCAL
    freopen ("input.txt", "r", stdin);
#endif
    scanf ("%d", &n);
    for (int i = 1; i < n; i++) {
        int x, y, z;
        scanf ("%d %d %d", &x, &y, &z);
        G[x].pb (mp (y, z) );
        G[y].pb (mp (x, z) );
    }
    for (int i = 1; i <= n; i++) dijstral (i);
    dfs1 (1, 0);
    d2[1] = d1[1];
    dfs2 (1, 0);
    for (int i = 1; i <= n; i++) tot += d2[i];
    ans = tot;
    dfs3(1, 0);
    printf("%lld\n", ans / 2);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/c6376315qqso/article/details/82694893