EOJ Monthly 2018.8 - D - Delivery Service
题意:
有一棵n个节点的树,树的每条边都有各自的权值,在询问开始前,可以交换任一两条边的权值,次数不限,有q个询问,每个询问包含一个起点和终点,从起点到终点经过的边的权值和就是这次询问的花费,求所有询问的最小花费和。
换边只能在询问前。
因为换边只能在询问前,而且可以任意换边,问题就转换为,求出每条边被经过的次数,然后最多的边给最小的权值。这样一来总的花费最小。然后就是求每条边的经过次数即可。
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long int LL;
const int N = 4e5 + 10;
int n, q, x, y, z;
int nt[N], head[N], w[N], id[N], to[N], sz;
int fa[N][20], dep[N];
LL sum[N], ww[N], cnt[N];
void add_e(int u, int v, int i){
nt[++sz] = head[u]; to[sz] = v; id[sz] = i; head[u] = sz;
}
void dfs(int x, int pre, int d){
dep[x] = d;
for(int i=head[x]; i; i=nt[i]){
if(to[i] == pre) continue;
dfs(to[i], x, d+1);
fa[to[i]][0] = x;
}
}
void init(){
dfs(1, 0, 1);
for(int i=1;i<20;i++){
for(int j=1;j<=n;j++){
if(fa[j][i-1]==0 || fa[fa[j][i-1]][i-1] == 0) fa[j][i] = 0;
else fa[j][i] = fa[fa[j][i-1]][i-1];
}
}
}
int lca(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
for(int i=19; i>=0; i--){
if(dep[fa[x][i]] > dep[y]) x = fa[x][i];
}
if(dep[x] != dep[y]) x = fa[x][0];
for(int i=19;i>=0;i--){
if(fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
if(x != y) x = fa[x][0];
return x;
}
int dfs2(int x, int pre){
int ans = 0;
for(int i=head[x]; i; i=nt[i]) {
if(to[i] == pre) continue;
int k = dfs2(to[i], x);
sum[id[i]] += k;
ans += k;
}
return ans + cnt[x];
}
int main()
{
scanf("%d", &n);
for(int i=1;i<n;i++){
scanf("%d%d%d", &x, &y, &z);
add_e(x, y, i); add_e(y, x, i); w[i] = z;
}
init();
scanf("%d", &q);
while(q--) {
scanf("%d%d", &x, &y);
int aim = lca(x, y);
cnt[aim] -= 2; cnt[x] ++; cnt[y] ++;
}
dfs2(1, 0);
sort(sum+1, sum+n); sort(w+1, w+n);
LL ans = 0;
for(int i=1;i<n;i++) ans += 1LL * sum[i] * w[n-i];
printf("%lld\n", ans);
return 0;
}