您的位置:首页 > 其它

【清华集训2017模拟12.09】Tree

2017-12-19 16:59 375 查看
Description



Input



Output

一行一个整数, 表示最小的距离和。

Sample Input

10 7

1 2 35129

2 3 42976

3 4 24497

2 5 83165

1 6 4748

5 7 38311

4 8 70052

3 9 3561

8 10 80238

Sample Output

184524

Data Constraint



题解

设g[i][j] 为以i为根的子树选择了j个点,它们构成的虚树的总长的最小值

f0[i][j]为i为根选了j个点,且当前的最长链的一个端点的i的2*边-链长的最小值

f1定义与f0相似,但是链不过端点(其实也可以过,但是不会比对应的f1更优,所以其实没有影响)

然后就按照链在子树内的情况讨论dp就可以了

看上去好像是n^3的,但是实际上复杂度是∑fa[x]=fa[y]size[x]∗size[y]把它拆开时候发现是n^2的

贴代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fo1(i,b,a) for(i=b;i>=a;i--)
#define min(x,y) ((x)<(y)?(x):(y))
#define ll long long
using namespace std;

const int maxn=3005;

int fi[maxn],ne[maxn*2],dui[maxn*2],dui1[maxn*2],qc[maxn],s[maxn];
ll f0[maxn][maxn],f1[maxn][maxn],g[maxn][maxn];
int i,j,k,l,m,n,x,y,z,now;
ll ans,b;

void add(int x,int y){
if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
qc[x]=now; dui[now]=y; dui1[now]=z;
}
void dfs(int x,int y){
int i=fi[x];
s[x]=1; g[x][1]=f0[x][1]=f1[x][1]=0;
while (i){
if (dui[i]==y){
i=ne[i];
continue;
}
dfs(dui[i],x); z=dui[i];
fo1(j,min(m,s[x]),1){
fo1(k,min(s[dui[i]],m-j),1){
f1[x][j+k]=min(f1[x][j+k],b*g[x][j]+f1[z][k]+b*dui1[i]);
f1[x][j+k]=min(f1[x][j+k],f0[x][j]+f0[z][k]+dui1[i]);
f1[x][j+k]=min(f1[x][j+k],f1[x][j]+b*g[z][k]+b*dui1[i]);
f0[x][j+k]=min(f0[x][j+k],b*g[x][j]+f0[z][k]+dui1[i]);
f0[x][j+k]=min(f0[x][j+k],f0[x][j]+b*g[z][k]+b*dui1[i]);
g[x][j+k]=min(g[x][j+k],g[x][j]+g[z][k]+dui1[i]);
}
}
s[x]=s[x]+s[dui[i]];
i=ne[i];
}
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,n-1){
scanf("%d%d%d",&x,&y,&z);
add(x,y); add(y,x);
}
memset(g,1,sizeof(g));
memset(f0,1,sizeof(f0));
memset(f1,1,sizeof(f1)); b=2;
dfs(1,0);
ans=min(f0[1][m],f1[1][m]);
fo(i,2,n){
ans=min(ans,min(f0[i][m],f1[i][m]));
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: