题意:
给你一颗带权无向树,你可以改变一条边的位置,使得改变后任意两点仍然可达,问改变后任意两点路径的和的最小值是多少?
题解:
随便推一推就可以了,思路很简单,只是推的时候一堆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;
}