正题
题目链接:https://www.luogu.com.cn/problem/P4383
题目大意
nnn个点的一棵树,要求删除kkk条边然后接上kkk条边权为000的边后形成的树上选择一对(p,q)(p,q)(p,q)从ppp走简单路径到qqq的权值和最大。
n,k≤3×105n,k\leq 3\times 10^5n,k≤3×105
解题思路
其实可以理解为选恰好k+1k+1k+1条不相交的路径(可以选择一个点)使得权值和最大,这样删除路径最顶部的那条边一定有方案构造。
因为是恰好选择,所以考虑wqswqswqs二分,给每条路径加上一个权值midmidmid,然后考虑用树形dpdpdp做就很简单了。设fi,0/1/2f_{i,0/1/2}fi,0/1/2表示iii号点没有路径经过/在路径之间/作为路径顶部时子树内的最小权值,然后转移即可。
时间复杂度:O(nlogW)O(n\log W)O(nlogW)
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;
}