入门BZOJ3004 [Noi2016十连测第一场]访问计划

题面在这里

如果没有传送的话,每条边的贡献是两倍边权

考虑一次传送,其实是把一条路径上的边权和用c代替

每条边至少走一次,所以路径不能有交

那么问题就转化为求一棵树选K条路径,使得边权和最大

树形背包即可

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<cstdio>
#include<cstring>
#include<algorithm>
#define cl(x,y) memset(x,y,sizeof(x))
using namespace std;

const int maxn=2005,INF=0x3f3f3f3f;
struct edge{int to,w,nt;}s[maxn*2];
int n,cnt,cost,num,h[maxn],sz[maxn];
int f[maxn][maxn][2],g[maxn][2],ans;
inline void add(int x,int y,int w){
s[++num]=(edge){y,w,h[x]},h[x]=num;s[++num]=(edge){x,w,h[y]},h[y]=num;
}
inline void DP(int x,int fa,int sum){
bool mk=1;sz[x]=1;
for (int i=h[x];i;i=s[i].nt)
if (s[i].to!=fa){
DP(s[i].to,x,s[i].w),sz[x]+=sz[s[i].to];
if (mk)
for (int k=0;k<=sz[s[i].to];k++){
f[x][k][0]=min(f[x][k][0],f[s[i].to][k][0]);
f[x][k][1]=min(f[x][k][1],f[s[i].to][k][1]);
if (k+1<=cnt) f[x][k+1][0]=min(f[x][k+1][0],f[s[i].to][k][1]);
}
else{
cl(g,63);
for (int xk=0;xk<=sz[x];++xk){
if (f[x][xk][0]!=INF){
int base=f[x][xk][0],lim=min(cnt-xk,sz[s[i].to]);
for (int tk=0;tk<=lim;++tk){
g[xk+tk][0]=min(g[xk+tk][0],base+f[s[i].to][tk][0]);
g[xk+tk][1]=min(g[xk+tk][1],base+f[s[i].to][tk][1]);
if (xk+tk+1<=cnt)
g[xk+tk+1][0]=min(g[xk+tk+1][0],base+f[s[i].to][tk][1]);
}
}
if (f[x][xk][1]!=INF){
int base=f[x][xk][1],lim=min(cnt-xk,sz[s[i].to]);
for (int tk=0;tk<=lim;++tk){
g[xk+tk][1]=min(g[xk+tk][1],base+f[s[i].to][tk][0]);
if (xk+tk+1<=cnt){
g[xk+tk+1][0]=min(g[xk+tk+1][0],base+f[s[i].to][tk][1]-cost);
g[xk+tk+1][1]=min(g[xk+tk+1][1],base+f[s[i].to][tk][1]);
}
}
}
}
for (int k=0;k<=cnt;k++) f[x][k][0]=g[k][0],f[x][k][1]=g[k][1];
}
mk=0;
}
if (mk) f[x][0][0]=sum<<1,f[x][0][1]=cost+sum;
else {
for (int k=0;k<=cnt;k++) f[x][k][1]=min(f[x][k][1],f[x][k][0]+cost);
for (int k=0;k<=cnt;k++)
for (int j=0;j<=1;j++)
f[x][k][j]+=(2-j)*sum,f[x][k][j]=min(f[x][k][j],INF);
}
}

int main(){
while (~scanf("%d%d%d",&n,&cnt,&cost)){
cl(h,0);cl(f,63);
num=0,ans=INF;
for (int i=1,x,y,w;i<n;i++)
scanf("%d%d%d",&x,&y,&w),++x,++y,add(x,y,w);
DP(1,0,0);
for (int i=0;i<=cnt;i++) ans=min(ans,f[1][i][0]);
printf("%d\n",ans);
}
return 0;
}