zoukankan      html  css  js  c++  java
  • [BZOJ 5252][LOJ 2478][九省联考2018] 林克卡特树

    [BZOJ 5252][LOJ 2478][九省联考2018] 林克卡特树

    题意

    给定一个 (n) 个点边带权的无根树, 要求切断其中恰好 (k) 条边再连 (k) 条边权为 (0) 的边重新连成一棵树, 最大化新树上某条路径的权值和.

    (0le k<nle 3 imes 10^5). 边权的绝对值不超过 (1 imes 10^6).

    提示: 题目并不难

    题解

    当时场上做这题的时候根本不知道有wqs二分这种高端套路...看到提示之后果断跑路了qaq...

    首先切断 (k) 条边再连 (k)(0) 权边并最大化一条路径的权值和显然相当于最大化在树上选 (k+1) 条点不相交路径的权值和.

    这种恰好选若干条的一般考虑wqs二分. 二分一个附加权值, 每次多选一条链就会多产生一些权值, 然后DP的时候就可以变成 "在树中选若干条链并最大化权值和". 定义 (dp_{i,0/1/2}). 为以 (i) 为根的子树中, (i) 号点度数为 (0/1/2) 时的最大权值. 注意单点也可以作为一条合法的链, 我们可以看成一条自环贡献两个度数.

    那么分几种情况讨论就好了. 度数小的状态可以通过向子树连一条边的转移到度数大的状态. 注意其中链的数量的增减就好了.

    以及wqs二分在DP的时候需要顺便计数, 计数部分用结构体来写会好写很多(而且好像跑得还更快)

    还有就是注意在值相等的时候对于链的数量的选择要有侧重. 一般倾向于统一选多的, 然后在二分循环里具体问题具体分析.

    参考代码

    #include <bits/stdc++.h>
    
    const int MAXV=3e5+10;
    const int MAXE=1e6+10;
    typedef long long intEx;
    const intEx INF=1e15;
    
    struct Edge{
        int from;
        int to;
        int dis;
        Edge* next;
    };
    Edge E[MAXE];
    Edge* head[MAXV];
    Edge* top=E;
    
    struct Data{
        intEx sum;
        int cnt;
        Data(){}
        Data(const intEx& s,int c):sum(s),cnt(c){}
        Data friend operator+(const Data& a,const Data& b){
            return Data(a.sum+b.sum,a.cnt+b.cnt);
        }
        Data friend operator-(const Data& a,const Data& b){
            return Data(a.sum-b.sum,a.cnt-b.cnt);
        }
        bool friend operator<(const Data& a,const Data& b){
            return a.sum==b.sum?a.cnt<b.cnt:a.sum<b.sum;
        }
    };
    
    int n;
    int k;
    Data add;
    Data dp[MAXV][3];
    
    void DFS(int,int);
    void Insert(int,int,int);
    
    int main(){
        scanf("%d%d",&n,&k);
        for(int i=1;i<n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            Insert(a,b,c);
            Insert(b,a,c);
        }
        add.cnt=1;
        intEx l=-1e12,r=1e12;
        while(r-l>1){
            intEx mid=(l+r+1)>>1;
            add.sum=mid;
            DFS(1,0);
            Data cur=std::max(dp[1][0],std::max(dp[1][1],dp[1][2]));
            if(cur.cnt>=k+1)
                r=mid;
            else
                l=mid;
        }
        add.sum=r;
        DFS(1,0);
        Data cur=std::max(dp[1][0],std::max(dp[1][1],dp[1][2]));
        printf("%lld
    ",cur.sum-(k+1)*r);
        return 0;
    }
    
    void DFS(int root,int prt){
        dp[root][0]=Data(0,0);
        dp[root][1]=Data(-INF,0);
        dp[root][2]=add;
        for(Edge* i=head[root];i!=NULL;i=i->next){
            if(i->to!=prt){
                DFS(i->to,root);
                Data mx=std::max(dp[i->to][0],std::max(dp[i->to][1],dp[i->to][2]));
                dp[root][2]=std::max(dp[root][2]+mx,dp[root][1]+Data(i->dis,0)+std::max(dp[i->to][0],dp[i->to][1]-add));
                Data tmp=dp[root][0]+Data(i->dis,0)+std::max(dp[i->to][0]+add,dp[i->to][1]);
                dp[root][1]=std::max(dp[root][1]+mx,tmp);
                dp[root][0]=dp[root][0]+mx;
            }
        }
    }
    
    inline void Insert(int from,int to,int dis){
        top->from=from;
        top->to=to;
        top->dis=dis;
        top->next=head[from];
        head[from]=top++;
    }
    
    

  • 相关阅读:
    解决ORA14098分区交换索引不匹配错误
    Oracle等待事件kfk:async disk IO
    Advanced Diagnostic using oradebug dumpvar
    shellscript 06 命令执行顺序
    Linux 备份策略
    shellscript 05 shell输入与输出
    Java video 02
    软件安装:原始码与 Tarball
    shellscript 07 正则表达式介绍
    shellscript 03 后台执行命令
  • 原文地址:https://www.cnblogs.com/rvalue/p/10483220.html
Copyright © 2011-2022 走看看