[八省联考2018]林克卡特树lct

题目的描述有点玄学。

但发现把一个树切成k+1个联通块后,再连接起来并且找一个最长的路径的方法,显然是这个k+1个联通块首尾相接,直径连直径。

进一步的,这个问题等价于在树上选择恰好k+1条链,不允许点相交,路径可以为一个点,使得路径长度之和最大。

这个东西首先可以用一个O(nk)的dp来搞,然而n<=1e5。

对于这类严格数量限制问题,可以用wqs二分来解决,二分一下偏移量,dp的时候保存一下当前的链的数量,数量越多越好。

如果权值最优方案中,选择的链的数量<k+1,则令l=mid+1,反之同理。

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<vector>
#include<cstdlib>
#include<algorithm>
#define N 1100000
#define eps 1e-7
#define ll long long
using namespace std;
inline ll read()
{
    char ch=0;
    ll x=0,flag=1;
    while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*flag;
}
const ll inf=1e9+7;
struct edge
{
    ll to,nxt,w;
}e[N*2];
ll num=-1,head[N];
inline void add(ll x,ll y,ll z)
{
    e[++num]=(edge){y,head[x],z};head[x]=num;
    e[++num]=(edge){x,head[y],z};head[y]=num;
}
struct node{ll x,t;}dp[N][3];
bool operator<(node a,node b){if(a.x!=b.x)return a.x<b.x;else return a.t<b.t;}
node operator+(node a,node b){return (node){a.x+b.x,a.t+b.t};}
void dfs(ll x,ll fa,ll k)
{
    dp[x][0]=(node){0,0};dp[x][1]=(node){-inf,-inf};dp[x][2]=(node){k,1};
    for(ll i=head[x];i!=-1;i=e[i].nxt)
    {
        ll to=e[i].to;
        if(to==fa)continue;
        dfs(to,x,k);
        node o=max(dp[to][0],max(dp[to][1],dp[to][2]));
        dp[x][2]=
        max
        (
            dp[x][2]+o,
            max
            (
                dp[x][1]+dp[to][1]+(node){e[i].w-k,-1},
                dp[x][1]+dp[to][0]+(node){e[i].w,0}
            )
        );
        dp[x][1]=
        max
        (
            dp[x][1]+o,
            max
            (
                dp[x][0]+dp[to][1]+(node){e[i].w,0},
                dp[x][0]+dp[to][0]+(node){e[i].w+k,1}
            )
        );
        dp[x][0]=dp[x][0]+o;
    }
}
int main()
{
    memset(head,-1,sizeof(head));
    ll n=read(),k=read()+1;
    for(ll i=1;i<=n-1;i++)
    {
        ll x=read(),y=read(),z=read();
        add(x,y,z);
    }
    ll l=-inf,r=+inf,mid;
    while(l<r)
    {
        mid=((l+r)>>1);
        dfs(1,1,mid);
        node o=max(dp[1][0],max(dp[1][1],dp[1][2]));
        if(o.t<k)l=mid+1;
        else r=mid;
    }
    dfs(1,1,l);
    printf("%lld",max(dp[1][0],max(dp[1][1],dp[1][2])).x-k*l);
    return 0;
}

转载于:https://www.cnblogs.com/Creed-qwq/p/10117283.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值