題目:
題解:
題目等價於:在樹上選擇k+條不相交的鏈,使其權值和最大。
考慮樹形DP(以下的k均爲k+1)
一個很直觀的想法是用f[i][j]表示第i個節點,子樹中選了j條鏈的最大價值。
但這樣是無法轉移的,因此我們要考慮到根節點的情況,
令f[0/1/2][i][j]表示i號節點的子樹中選了j條鏈,i節點度數爲0/1/2的最大值。
更新的時候分三種情況討論。
但是這樣複雜度是O(N∗K)的,考慮繼續優化。
看到這種帶k限制類的問題,我們嘗試wqs二分
按照套路,我們觀察f數組在不同的k下的最優解,不難發現函數是上凸的。嚴格證明不會,自己意會一下吧。。
按照套路,二分一個邊權,加到每條邊上,我們可以通過控制邊權來控制k的大小。如果邊權都很小,肯定是少選幾條鏈比較優,如果邊權比較大,肯定是多選幾條優。
按照套路,如果我們二分到一個邊權,在這裏恰好選了k條鏈,那麼這種選法就是最優的。
也就是說,我們對於每一條鏈添加上一個mid的代價,用來調控選擇鏈的數量
判斷的時候不需要枚舉k,因此DP一遍的複雜度爲O(N),總複雜度爲O(NlogV)
還有就是這玩意兒二分的邊界比較詭異。。。
代碼:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 1e9
#define LL long long
using namespace std;
const int N=300005;
int tot,nxt[N*2],point[N],v[N*2];LL c[N*2],mid;
struct hh
{
LL x,y;
bool operator <(const hh &a) const {return x<a.x;}
hh operator +(const hh &a) const {return (hh){x+a.x,y+a.y};}
hh operator +(const int &bl) const {return (hh){x+bl,y};}
}f[3][N];
void addline(int x,int y,LL z)
{
++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y; c[tot]=z;
++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x; c[tot]=z;
}
hh add(hh a){return (hh){a.x-mid,a.y+1};}
void dfs(int x,int fa)
{
for (int i=point[x];i;i=nxt[i])
if (v[i]!=fa)
{
dfs(v[i],x);
f[2][x]=max(f[2][x]+f[0][v[i]],add(f[1][x]+f[1][v[i]]+c[i]));
f[1][x]=max(f[1][x]+f[0][v[i]],f[0][x]+f[1][v[i]]+c[i]);
f[0][x]=f[0][x]+f[0][v[i]];
}
f[0][x]=max(f[0][x],max(add(f[1][x]),max(f[2][x],(hh){-mid,1})));
}
int main()
{
int n;LL K;scanf("%d%lld",&n,&K);K++;
LL l,r=0;
for (int i=1;i<n;i++)
{
int x,y;LL z;scanf("%d%d%lld",&x,&y,&z);addline(x,y,z);
r+=abs(z);
}
l=-r;
while (l<=r)
{
mid=(l+r)>>1;
memset(f,0,sizeof(f));
dfs(1,0);
if (f[0][1].y<=K) r=mid-1;
else l=mid+1;
}
mid=l;memset(f,0,sizeof(f));
dfs(1,0);
printf("%lld",f[0][1].x+K*mid);
}