https://acm.ecnu.edu.cn/contest/103/problem/D/
题意:
给你一棵树,n-1条边和权值,给你q个起点和终点。
你可以在【最初】无限次交换边的权值,问你最小花费是多少。
POINT:
这个问题其实就是求每条边被走过了几次。然后按照从多到少分别给从小到大的权值即可。
前面一个问题就是树上差分问题。
#include <stdio.h>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
const int N = 2e5+22;
int head[N],to[N<<1],nxt[N<<1],cnt=0;
int fa[N][30],d[N],n;
void add(int x,int y)
{
to[cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt++;
}
void dfs(int u,int pre)
{
d[u]=d[pre]+1;
for(int i=head[u];~i;i=nxt[i]){
int v=to[i];
if(v==pre) continue;
dfs(v,u);
fa[v][0]=u;
}
}
void init()
{
for(int i=1;(1<<i)<=n;i++){
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
int query(int a,int b)
{
if(d[a]<d[b]) swap(a, b);
int dis=d[a]-d[b];
for(int i=0;i<=(int)log2(n);i++){
if(1<<i&dis){
a=fa[a][i];
}
}
if(a==b) return a;
for(int i=(int)log2(n);i>=0;i--){
if(fa[a][i]!=fa[b][i]){
a=fa[a][i];
b=fa[b][i];
}
}
return fa[a][0];
}
int val[N];
int w[N];
int c[N],k=0;
int dfs1(int u,int pre)
{
int ans=val[u];
for(int i=head[u];~i;i=nxt[i]){
int v=to[i];
if(v==pre) continue;
ans+=dfs1(v,u);
}
c[++k]=ans;
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) head[i]=-1;
for(int i=1;i<n;i++){
int u,v;scanf("%d%d%d",&u,&v,&w[i]);
add(u,v);
add(v,u);
}
dfs(1,0);
init();
int q;scanf("%d",&q);
while(q--){
int u,v;scanf("%d%d",&u,&v);
val[u]++;val[v]++;
val[query(u,v)]-=2;
}
dfs1(1,-1);
sort(w+1,w+n);
LL ans=0;
sort(c+1,c+k);
for(int i=1;i<k;i++){
ans+=w[i]*c[k-i];
}
printf("%lld\n",ans);
}