cogimyunの小窝

Loading...

P4383 [八省联考 2018] 林克卡特树 题解

题意

给你一棵 \(n\) 个点的树 \(T\),边有正负权值,要求切断原树 \(T\) 中的 \(k\) 条边,然后新添加 \(k\) 条权值为 0 的边使其成为一棵新树 \(T'\),求树 \(T'\) 的直径最大是多少。

题意转化

我们发现切割的操作将树 \(T\) 分为了 \(k+1\) 个连通块,而连边操作则可以帮我们将在原树 \(T\) 上不交的链连接在一起,于是题目就变成了求树 \(T\)\(k+1\) 条不交链的最大权值和。

Solution1

我们先考虑最朴素的树形 dp,记 \(f_{i,j,k}\) 表示在节点 \(i\) 时有 \(j\) 条链,且节点 \(i\) 在自己所在链中的度数为 \(k\),显然 \(k=0,1,2\)。接下来考虑以 \(x\) 为根的子树到 \(i\) 的转移,记 \(i\)\(x\) 之间的边权为 \(w\)

  1. 我们发现,\(\forall k=1,2,3\) 都可以直接继承子树中的链,即:

    \[t=\max(f_{x,y,0},f_{x,y,1},f_{x,y,2}) \]

    \[f_{i,j,k}=\max(f_{i,j,k},f_{i,j-y,k}+t) \]

  2. 继续考虑 \(k=1\) 时的转移,发现 \(i\) 的这条链可以是 \(i\)\(x\) 之间的链,也可以是 \(i\)\(x\)\(k=1\) 时的链连接在一起,即:

\[f_{i,j,1}=\max(f_{i,j,1},f_{i,j-y,0}+w+\max(f_{x,y-1,0},f_{x,y,1})) \]

  1. 最后考虑 \(k=2\) 时的转移,发现 \(i\) 的这条链可以是 \(i\)\(k=1\) 时的链与 \(i\)\(x\) 之间的链组合而成,也可以是 \(i\)\(k=1\) 时的链与 \(x\)\(k=1\) 时的链连接在一起,即:

\[f_{i,j,2}=\max(f_{i,j,2},f_{i,j-y,1}+w+\max(f_{x,y,0},f_{x,y+1,1})) \]

我们发现虽然是按照 \(k\) 从小到大讨论的转移,但是 \(f\)\(k\) 更大时的转移需要用到 \(f\)\(k\) 更小时的值,所以应该按 \(k\) 从大到小转移,或者用临时变量存下来,这个算法的时间复杂度是 \(O(nk^2)\)

Solution2

考虑优化上面的算法,链不交而且要求权值最大启发我们使用 wqs 二分,你感性理解,发现我们应该会先选长的不交链再选短的不交链,即 \(f\) 是关于 \(k\) 上凸的,所以 wqs 二分可以优化掉 \(k\) 的限制,使得单次 \(dp\) 的时间复杂度为 \(O(n)\),具体实现与 Solution1 基本类似,就不加以赘述,最后该算法时间复杂度应该是 \(O(n\log V)\)\(V\) 大约是 \(10^{12}\) 级别的。

Code

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int inf=1e12;
int n,k;
vector<pair<int,int>> e[300005];
struct node{
    int val,k;
}f[300005][3],tr[3];
node operator+(node x,node y){return node{x.val+y.val,x.k+y.k};}
bool operator<(node x,node y){return ((x.val<y.val||(x.val==y.val&&x.k>y.k)));}
template<typename T> inline T maxk(T x,T y){return (x<y)?y:x;}
template<typename T,typename ...T1> inline T maxk(T x,T y,T1...z){return (x<y)?maxk(y,z...):maxk(x,z...);}
void dfs(int x,int fa,int c){
    f[x][0]={0,0},f[x][1]={-inf,inf},f[x][2]={-c,1};
    for(auto [i,j]:e[x])if(i!=fa){
        dfs(i,x,c);
        tr[0]=tr[1]=tr[2]={-inf,inf};
        auto t=maxk(f[i][0],f[i][1],f[i][2]);
        tr[0]=maxk(tr[0],f[x][0]+t);
        tr[1]=maxk(tr[1],f[x][1]+t);
        tr[2]=maxk(tr[2],f[x][2]+t);
        tr[1]=maxk(tr[1],f[x][0]+f[i][0]+node{j-c,1});
        tr[1]=maxk(tr[1],f[x][0]+f[i][1]+node{j,0});
        tr[2]=maxk(tr[2],f[x][1]+f[i][0]+node{j,0});
        tr[2]=maxk(tr[2],f[x][1]+f[i][1]+node{j+c,-1});
        f[x][0]=tr[0],f[x][1]=tr[1],f[x][2]=tr[2];
    }
}
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n>>k;
    for(int i=1;i<n;i++){
        int x,y,z;
        cin>>x>>y>>z;
        e[x].push_back(make_pair(y,z));
        e[y].push_back(make_pair(x,z));
    }
    int l=-1e12,r=1e12;
    while(l<=r){
        int mid=(l+r)>>1;
        dfs(1,1,mid);
        auto [x,y]=maxk(f[1][0],f[1][1],f[1][2]);
        if(y==k+1){cout<<(k+1)*mid+x;return 0;}
        if(y>k+1)l=mid+1;else r=mid-1;
    }
    dfs(1,1,l);
    auto [x,y]=maxk(f[1][0],f[1][1],f[1][2]);
    cout<<(k+1)*l+x;
    return 0;
}
posted @ 2026-01-15 16:04  cogimyun  阅读(1)  评论(0)    收藏  举报