题意:
找出一个点,使得从这个点出发经过规定点规定次数路径总和最短
思路:
首先先建树,设1为根,并计算从1出发的总路径
那么从父结点递推下去算其字节点有:
dp[v] = dp[u]+(sum-f[v])*len*2-f[v]*len*2 式子的意思就是v以上的点(-f[v])要多走u->v, 然后呢由于dp[u]里面含有v以下的点v->u的路径,所以减去
代码:
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
#define INF 1000000000000000LL
const int maxn = 50010;
int n, m, num, cas, u, v;
int head[maxn], nex[maxn*2], ev[maxn*2], temp[maxn];
long long fre[maxn], f[maxn], ew[maxn*2], dp[maxn], dis[maxn];
long long w, sum, ans;
void aedge(int u, int v, long long w) {
nex[++num] = head[u];
head[u] = num;
ev[num] = v;
ew[num] = w;
}
void pre_dfs(int fa, int u) {
dis[u] = 0, f[u] = 0;
for(int i=head[u]; i!=-1; i=nex[i]) {
if(ev[i] != fa) {
pre_dfs(u, ev[i]);
dis[u] += dis[ev[i]] + ew[i]*2*f[ev[i]];
f[u] += f[ev[i]];//起始点累积所有的次数
}
}
f[u] += fre[u];
}
void dfs(int fa, int u, long long dis) {
dp[u] = dis;
if(dp[u] < ans) ans = dp[u];
for(int i = head[u]; i!=-1; i=nex[i]) {
if(fa != ev[i])
dfs(u, ev[i], dis+2*ew[i]*(sum-2*f[ev[i]]));
}
}
void deal() {
pre_dfs(1, 1);
ans = INF;
dfs(1, 1, dis[1]);
printf("%lld\n", ans);
int cnt = -1;
for(int i=1; i<=n; i++) {
if(dp[i] == ans)
temp[++cnt] = i;
}
for(int i=0; i<=cnt; i++) {
if(i != cnt) printf("%d ", temp[i]);
else printf("%d\n", temp[i]);
}
}
int main() {
scanf("%d", &cas);
while(cas--) {
memset(head, -1, sizeof(head));
memset(fre, 0, sizeof(fre));
num = -1;
scanf("%d", &n);
for(int i=0; i<n-1; i++) {
scanf("%d%d%lld", &u, &v, &w);
aedge(u, v, w);
aedge(v, u, w);
}
sum = 0;
scanf("%d", &m);
for(int i=0; i<m; i++) {
scanf("%d%lld", &u, &w);
fre[u] = w;
sum += fre[u];
}
deal();
}
return 0;
}