八省联考林克卡特树【题解】

9 篇文章 0 订阅
1 篇文章 0 订阅

前言

为了练习凸优化而做的

sol

我们发现,按照流程,先减去k条边,就会剩下k+1个联通块,然后再用k条边把这k+1个联通块连起来,这就是它构树的全过程.
我们发现,在重连k条边时,当前最长的路径已经出来了,就是每一个联通块内选一条最长的路径,然后把他们首尾相连。然后,恭喜获得暴力分,暴力就是暴力枚举减去哪k条边,然后求剩下的联通块每一条最长路径的和.注意到这里一个点算一个路径长为0的路径.
暴力过后,思考正解。
显然有一种想法,就是换一个枚举的方式,就是枚举出k+1条不相交的边,然后求和,最后保留那个和最大的情况。这样,就要求我们在树上求出k+1条不相交的路径,使得和最大.
显然,正解应该来自分治,dp,而不是dfs。考虑分治,我们发现要选出k+1条,必须要记录选了几条这个状态,分治只能选一条,所以分治不行.
这样就有了dp。
树型dp套路就是一个一个子树的考虑,然后把子树答案合并在一起。
一般树型dp合并答案都是一个难点.
这里也是。j已经表示了选了几条路径,所以还要增一维表示一个东西。
我们发现,跨子树连路径,一定要考虑:
1. 可不可以跨
2. 跨了以后权值怎么算
跨子树就是要跨子树的根,所以看一下某一种情况下子树的根的情况就可以了,所以用0,1,2表示根的度数。0表示没有一条路径,1表示作为一条路径的端点,2表示被一条路径穿过.
虽然0,2都代表不能,但0能转移到1,1能转移到2,这就是区别.

显然会有一大波讨论

dp[fa][j][0]=maxj=jj=0(dp[son][j][0|1|2]+dp[fa][jj][0]) d p [ f a ] [ j ] [ 0 ] = m a x j ′ = 0 j ′ = j ( d p [ s o n ] [ j ′ ] [ 0 | 1 | 2 ] + d p [ f a ] [ j − j ′ ] [ 0 ] )

dp[fa][j][1]=maxj=jj=0(dp[son][j][1]+dis+dp[fa][jj][0]) d p [ f a ] [ j ] [ 1 ] = m a x j ′ = 0 j ′ = j ( d p [ s o n ] [ j ′ ] [ 1 ] + d i s + d p [ f a ] [ j − j ′ ] [ 0 ] )

dp[fa][j][1]=maxj=jj=0(dp[son][j][0|1|2]+dp[fa][jj][1]) d p [ f a ] [ j ] [ 1 ] = m a x j ′ = 0 j ′ = j ( d p [ s o n ] [ j ′ ] [ 0 | 1 | 2 ] + d p [ f a ] [ j − j ′ ] [ 1 ] )

dp[fa][j][2]=maxj=jj=0(dp[son][j][1]+dis+dp[fa][jj][1]) d p [ f a ] [ j ] [ 2 ] = m a x j ′ = 0 j ′ = j ( d p [ s o n ] [ j ′ ] [ 1 ] + d i s + d p [ f a ] [ j − j ′ ] [ 1 ] )

dp[fa][j][2]=maxj=jj=0(dp[son][j][0|1|2]+dp[fa][jj][2]) d p [ f a ] [ j ] [ 2 ] = m a x j ′ = 0 j ′ = j ( d p [ s o n ] [ j ′ ] [ 0 | 1 | 2 ] + d p [ f a ] [ j − j ′ ] [ 2 ] )

然后就是一波凸优化。
凸优化的论文网上都有,实际上搜带权二分更好
这里简单的说就是原本的dp是dp[i][j],表示做到第i个,一定要满足j的条件的最大值.
大佬觉得不顺眼,强行写成dp[i],表示做到第i个,不要满足其他任何条件的最大值。
这样这个dp就好写了。
大佬这么做是发现,dp[n][x]是一个上凸函数,x是自变量;
所以,这道题圆满的解决了

code


#include<bits/stdc++.h>
#define LL long long
using namespace std;
const LL INF = 2e16;
template <class T>
inline void read(T&data){
    data=0;
    register char ch=0;
    register int caa=1;
    while(ch!='-'&&(ch>'9'||ch<'0'))ch=getchar();
    ch=='-'?caa=-1,ch=getchar():caa=1;
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch^48);
        ch=getchar();
    }data*=caa;
    return;
}
template<class R>
inline void write(R data){
    if(data>9)write(data/10);
    putchar(data+'0');
}
const int _ (3e5+10);
struct zjy{
    LL zh,cost;
}dp[_][3],g[3];
LL res;
int to[_<<1],dis[_],nxt[_<<1],w[_<<1],head[_],n,k,cnt;
LL ans;
inline void add(register int a,register int b,register int c){to[++cnt]=b,nxt[cnt]=head[a],w[cnt]=c,head[a]=cnt;}
inline zjy mx(register zjy a,register zjy b){
    if(a.zh>b.zh)return a;
    return b;
}
void Dp(register int now,register int fa){
    dp[now][0].zh=0,dp[now][0].cost=0;
    dp[now][2].zh=-res,dp[now][2].cost=1;
    dp[now][1].zh=-res;dp[now][1].cost=1;
    for(register int i=head[now];i;i=nxt[i]){
        if(to[i]==fa)continue;
        Dp(to[i],now);
        g[0]=dp[now][0],g[1]=dp[now][1],g[2]=dp[now][2];
        register zjy ele= mx(dp[to[i]][0],mx(dp[ to[i] ][1] ,dp[ to[i] ][ 2 ] ) );
        if(g[0].zh<dp[now][0].zh+ele.zh) g[0].zh=dp[now][0].zh+ele.zh,g[0].cost=dp[now][0].cost+ele.cost;       
        if(g[1].zh<dp[now][0].zh+dp[to[i]][1].zh+w[i]) g[1].zh=dp[now][0].zh+dp[to[i]][1].zh+w[i],g[1].cost=dp[now][0].cost+dp[to[i]][1].cost;
        if(g[1].zh<dp[now][1].zh+ele.zh)g[1].zh=dp[now][1].zh+ele.zh,g[1].cost=dp[now][1].cost+ele.cost;
        if(g[2].zh<dp[now][2].zh+ele.zh)g[2].zh=dp[now][2].zh+ele.zh,g[2].cost=dp[now][2].cost+ele.cost;
        if(g[2].zh<dp[now][1].zh+dp[to[i]][1].zh+w[i]+res)g[2].zh=dp[now][1].zh+dp[to[i]][1].zh+res+w[i],g[2].cost=dp[now][1].cost+dp[to[i]][1].cost-1;
        dp[now][1]=g[1],dp[now][2]=g[2],dp[now][0]=g[0];
    }
}
inline bool check(register LL cost){
    res=cost;
    Dp(1,0);
    register zjy ele =mx(dp[1][0] ,mx(dp[1][1],dp[1][2]));
    //ans=ma;
    return ele.cost<=k;
}
int main(){
    freopen("data.in","r",stdin);
    freopen("1.out","w",stdout);
    read(n),read(k);
    register LL a,b,c,l=0,r=0,mid;++k;
    for(register int i=1;i<n;++i)
        read(a),read(b),read(c),add(a,b,c),add(b,a,c),r+=abs(c);
    cnt=0;
    l=-r;
    /*res=0;
    Dp(1,0);register zjy elew=mx(dp[1][0],mx(dp[1][1],dp[1][2]));
    //cout<<elew.zh<<' '<<elew.cost<<endl;
    cout<<dp[1][0].zh<<' '<<dp[1][1].zh<<' '<<dp[1][2].zh<<endl;
    exit(0);*/
    while(l<=r){
        mid=(l+r)>>1;
        if(check(mid))r=mid-1;
        else l=mid+1;
    }
    res=l;
    Dp(1,0);
    register zjy ele =mx(dp[1][0],mx(dp[1][1],dp[1][2]));
    printf("%lld",ele.zh+k*res);
//write(ele.zh-k*res);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值