关键词:树中选取最优点
题意:简化后的数学模型:
已知一棵含点权和边权的无根树,在树中选择一个点u使得:
∑ni=1value[i]∗d[u][i]
最小。
value[i]是i点的权值,d[u][i]是u点到i点的距离
解法:
1.以1为根化为有根树
2.求u=1的sum值
3.寻找1的子节点的sum值和1节点的sum值的关系,发现与子节点的权值和有关
拓展:
1.求
∑ni=1∑nj=1value[i]∗value[j]∗d[i][j]
设w[i]是第i条边的权值,该边的尾节点是v,sum[v]是以v为根的子树权值和,sumvalue是树的总权
上式=
∑n−1i=1w[i]∗sum[v]∗(sumvalue−sum[v])
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#include<map>
#define ll long long
using namespace std;
const int maxn = 50000+10;
int t;
ll n,m;
ll s[maxn],value[maxn];
ll sum;
ll dis[maxn],d[maxn];//dis[i]:i到根节点的距离 d[i]:以i节点作为live点的代价
ll ans[maxn],cnt,tmp;//cnt,待确定点的个数 tmp,待求点的代价
struct Edge{
ll to,w,next;
}edge[maxn<<1];
ll head[maxn],tot;
void add(ll u,ll v,ll w){
edge[tot].to=v,edge[tot].w=w,edge[tot].next=head[u],head[u]=tot++;
}
void dfs1(ll u,ll f){
s[u]=value[u];
for(ll i=head[u];i!=-1;i=edge[i].next){
ll v=edge[i].to,w=edge[i].w;
if(v==f) continue;
dis[v]=dis[u]+w;
dfs1(v,u);
s[u]+=s[v];
}
}
void dfs2(ll u,ll f){
for(ll i=head[u];i!=-1;i=edge[i].next){
ll v=edge[i].to,w=edge[i].w;
if(v==f) continue;
d[v]=d[u]-s[v]*w+(sum-s[v])*w;
dfs2(v,u);
}
}
int main(){
scanf("%d",&t);
while(t--){
scanf("%lld",&n);
memset(head,-1,sizeof(head));
tot=0;
memset(value,0,sizeof(value));
memset(dis,0,sizeof(dis));
memset(d,0,sizeof(d));
sum=0;
for(ll i=0;i<n-1;i++){
ll x,y,z; scanf("%lld%lld%lld",&x,&y,&z);
add(x,y,z),add(y,x,z);
}
scanf("%lld",&m);
for(ll i=1;i<=m;i++){
ll u,v;
scanf("%lld%lld",&u,&v);
value[u]=v;
sum+=value[u];
}
dfs1(1,0);
//tmp:以1作为live点的代价
for(ll i=1;i<=n;i++){
d[1]=d[1]+dis[i]*value[i];
}
dfs2(1,0);
cnt=0,tmp=0;
for(ll i=1;i<=n;i++){
if(i==1||d[i]<tmp){
cnt=0,tmp=d[i];
ans[cnt++]=i;
}
else if(d[i]==tmp){
ans[cnt++]=i;
}
}
printf("%lld\n",tmp*2);
for(ll i=0;i<cnt-1;i++){
printf("%lld ",ans[i]);
}
if(cnt>=1) printf("%lld\n",ans[cnt-1]);
}
return 0;
}