自己去找吧
转化之后大概就是给你一个有n个点的值,有边权,然后要你选出k+1条不相交的链,问选择的所有边的和的最大值
先考虑一下60分,k是<=100的,那么我们不妨设f[i][j][0/1/2]表示当前我已经做完了i的子树,点i的度数是0/1/2,对于这一颗子树我们最多能选择的边权和是多少
现在题目的关键是要恰好选择k+1条不相交的链,而选多了选少了答案都可能会变大,这逼迫着我们无法避免的要去设那个第二维,现在我们想要不去设那个第二维
之前做过一道题目是选择1到n一条路径,使得其前k大的边的和最小,在那一题里面,我们强制把每一条边先min(0,x-l),然后最后加上k*l,这样做的好处就是不需要具体的设出选了多少条边权大于等于l的边,那么这一题之中我们其实也可以类似的做法
我们设ans(x)表示当k=x时的答案,我们对其差分,发现差分后的数组是单调递减的,不妨对其做出一个图像,大概是一个山包一样的东西,我们其实就是要获得ans(k),但是这个点不一定是最高的,现在如果我们能使得它变成最高的那个点,那么就不用考虑选了多少个了,只用找最值就可以了
那么我们不妨设 ans’(x)=ans(x)+x*p 只要我们选择的这个p可以使得ans’(k)是ans’()中最高的那一个点,那么我们就可以直接dp了
现在的问题就变成了如何求p,前面说过差分后数组单调,所以说随着p的增加,函数ans’(x)的图像的最值对应的横坐标是不断往右移的(也就是说是单调的),那么其实我们可以二分这个增加值p
在具体的实现之中,我们可以把k+1条链当成k次合并
#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=3e5+5;
int fi[maxn],ne[maxn*2],dui[maxn*2],dui1[maxn*2],qc[maxn];
ll f[maxn][3],t1[3],l,r,mid;
int g[maxn][3],t2[3];
int i,j,k,x,y,z,n,now;
void add(int x,int y,int z){
if (fi[x]==0) fi[x]=++now; else ne[qc[x]]=++now;
dui[now]=y; dui1[now]=z; qc[x]=now;
}
void merge(int x,int y,int z){
int i,j,s2; ll s1;
fo(i,0,2){
t1[i]=f[y][2]+f[x][i]+mid;
t2[i]=g[y][2]+g[x][i]+1;
}
fo(i,0,2)
fo(j,0,2-i){
s1=f[x][i]+f[y][j];
if (j==1) s1=s1+z;
s2=g[x][i]+g[y][j];
if (s1>t1[i+j]){
t1[i+j]=s1; t2[i+j]=s2;
} else if (s1==t1[i+j] && s20,1){
if (t1[i]>t1[i+1]){
t1[i+1]=t1[i]; t2[i+1]=t2[i];
} else if (t1[i]==t1[i+1] && t2[i]0,2){
f[x][i]=t1[i]; g[x][i]=t2[i];
}
}
void dfs(int x,int y){
for(int i=fi[x];i;i=ne[i]) if (dui[i]!=y){
dfs(dui[i],x);
merge(x,dui[i],dui1[i]);
}
}
int main(){
freopen("lct.in","r",stdin);
freopen("lct.out","w",stdout);
scanf("%d%d",&n,&k);
fo(i,1,n-1){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
l=-3e11; r=1e6;
while (l1)/2;
memset(f,0,sizeof(f));
memset(g,0,sizeof(g));
dfs(1,0);
if (l==r || g[1][2]==k) break;
if (g[1][2]else r=mid-1;
}
printf("%lld\n",f[1][2]-mid*k);
return 0;
}