#include <bits/stdc++.h> using namespace std; //dp[u][p]表示以u为根节点的树,选取了p个重要结点的距离之和的最小值 const int maxn = 5e4+100; int head[maxn]; //存以i为起点的最近加入的一条边的存储位置 int tot; //边的编号 int dp[maxn][102]; //dp[u][p]:以u为根节点选取p个重要节点的最小权值 int k; bool good[maxn]; //是否是重要节点 int num[maxn]; //以u为根节点的树中重要节点的个数 int tree_u_v[102]; //临时变量区 struct Node{ //链式前向星 struct int to; //边的终点 int w; //权值 int next; //相同起点的上一次加入的边的存储位置 }edge[maxn*2]; void init(){ tot = 0; memset(head,-1,sizeof(head)); //将head初始化为-1 } void add_edge(int from, int to, int w){ //from起点, to终点, w权值 edge[tot].to=to; edge[tot].w=w; edge[tot].next=head[from]; //head[from]:上一次加入的边的位置 head[from]=tot++; //更新以from为起点的最新加入的边的编号 } void dfs(int u, int fa){ //u节点,fa:u的父亲 dp[u][0]=0; //选取0个重要节点,权值为0 if(good[u]){ //如果u本身是重要节点 dp[u][1]=0; //选自己,但是有一个节点,所以权值还是0 num[u]=1; } for(int i=head[u]; i!=-1; i=edge[i].next){ //遍历u的邻接点 if(edge[i].to == fa) continue; int v = edge[i].to; int w = edge[i].w; dfs(v,u); //dfs u的子节点 for(int j=0;j<=k;j++) tree_u_v[j] = dp[u][j]; //保留更新前的数据 int nU = min(k,num[u]); //最多不能超过k int nV = min(k,num[v]); num[u]+=num[v]; for(int j=0;j<=nU;++j){ //不明白 ! for(int t=0; t<=nV && t+j<=k; ++t) //j、t、状态转移方程 不理解 dp[u][j+t] = min(1ll*dp[u][j+t], 1ll*(k-t)*t*w + tree_u_v[j] + dp[v][t]); } //要乘 1ll 否则会溢出 } } int main(){ int n,m,x,y,d; scanf("%d %d %d",&n,&m,&k); for(int i=0;i<m;i++){ scanf("%d",&x); good[x]=true; } init(); for(int i=1;i<n;i++){ scanf("%d %d %d",&x,&y,&d); add_edge(x,y,d); add_edge(y,x,d); } memset(dp,0x3f,sizeof(dp)); dfs(1,-1); printf("%d ",dp[1][k]); return 0; }
先码住