P4383-[八省联考2018]林克卡特树【wqs二分,树形dp】
正题
题目链接:https://www.luogu.com.cn/problem/P4383
题目大意
\(n\)个点的一棵树,要求删除\(k\)条边然后接上\(k\)条边权为\(0\)的边后形成的树上选择一对\((p,q)\)从\(p\)走简单路径到\(q\)的权值和最大。
\(n,k\leq 3\times 10^5\)
解题思路
其实可以理解为选恰好\(k+1\)条不相交的路径(可以选择一个点)使得权值和最大,这样删除路径最顶部的那条边一定有方案构造。
因为是恰好选择,所以考虑\(wqs\)二分,给每条路径加上一个权值\(mid\),然后考虑用树形\(dp\)做就很简单了。设\(f_{i,0/1/2}\)表示\(i\)号点没有路径经过/在路径之间/作为路径顶部时子树内的最小权值,然后转移即可。
时间复杂度:\(O(n\log W)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=3e5+10;
struct edge{
ll to,next,w;
}a[N<<1];
struct node{
ll w,g;
}f[N][3];
ll n,k,tot,c,ls[N];
node operator+(node x,node y)
{return (node){x.w+y.w,x.g+y.g};}
node operator+(node x,ll y)
{return (node){x.w+y,x.g};}
node operator^(node x,ll y)
{return (node){x.w,x.g+y};}
node mx(node x,node y){
if(x.w==y.w)return (x.g>y.g)?x:y;
return (x.w>y.w)?x:y;
}
void addl(ll x,ll y,ll w){
a[++tot].to=y;
a[tot].next=ls[x];
a[tot].w=w;ls[x]=tot;
return;
}
void dfs(ll x,ll fa){
f[x][0]=f[x][1]=(node){0,0};f[x][2]=(node){c,1};
for(ll i=ls[x];i;i=a[i].next){
ll y=a[i].to;
if(y==fa)continue;dfs(y,x);
f[x][2]=mx(f[x][2]+f[y][0],(f[x][1]+f[y][1]+a[i].w+c)^1);
f[x][1]=mx(f[x][0]+f[y][1]+a[i].w,f[x][1]+f[y][0]);
f[x][0]=f[x][0]+f[y][0];
}
f[x][0]=mx(f[x][0],mx((f[x][1]+c)^1,f[x][2]));
return;
}
signed main()
{
scanf("%lld%lld",&n,&k);
ll sum=0;k++;
for(ll i=1;i<n;i++){
ll x,y,w;
scanf("%lld%lld%lld",&x,&y,&w);
addl(x,y,w);addl(y,x,w);sum+=abs(w);
}
ll l=-sum,r=sum;
while(l<=r){
ll mid=(l+r)>>1;
c=mid;dfs(1,0);
if(f[1][0].g<k)l=mid+1;
else r=mid-1;
}
c=l;dfs(1,0);
printf("%lld\n",f[1][0].w-c*k);
return 0;
}