zoukankan      html  css  js  c++  java
  • wqs二分

    今天模拟赛有一道林克卡特树,完全没有思路
    赛后想了一想,不就是求(k+1)条不相交的链,使其权值之和最大嘛,傻了。
    有一个最裸的(DP),设(f[i][j][k])表示在以(i)为根的子树中,选了(j)条链,(k=0)表示(i)不在链上,(k=1)表示(i)是链的一端,(k=2)表示(i)在链的中间
    这样就随便转移了,就是个(O(nk^2))的树上背包
    然后呢,又傻了,这能怎么优化?
    我先在这里Orz一下大佬BLUESKY007,没有学过wqs二分,发现了(f)数组关于(k)的单调性,一波二分直接A了 %%%%%%
    没错,我们需要用这个单调性来进行优化。据官方题解称,假设你闲着没事,把(k=0-100)的表打了一下,你就会发现这个上凸函数,但是如果并没有闲心,那我们就大胆的猜一下。
    (k)很小的时候,我们肯定先删负权边,这样最大权值和就增大了。当负权边不够用了怎么办,我们就只能开始删正权边,这种情况貌似比较复杂,先来看看正权边删的很多的情况。随着正权边越删越多,最大权值和肯定有一个下降的趋势,这样随着(k)的增大,(f)就呈现出一个先增后减的趋势,也许(f)是一个上凸函数?猜对啦,确实是的
    接下来我们需要一个叫wqs二分的优化方法,它经常被用于这样的问题:有(n)个带权物品,用满足一定限制的方法选(m)个,使得其权值和取最值,而且权值和的最值是关于(m)的凸函数。设在取(x)个物品时的权值和为(f(x)),那么(f(x))的图像大概长这个样子:

    那我们该怎么知道(f(m))呢,因为(f(x))是凸的,考虑用一条直线去切它。就像这样:

    这样我们就得到了一条斜率为(k),解析式为(y=kx+b)的直线,上下移动这条直线,你会发现在切点处的截距(b)是最大的:

    而且切点处(b=f(x)-kx),假设我们能找到最大的(b)并顺便记录切点的位置,不就能计算(f(x))的值了吗?观察(b)的表达式,发现如果我们给每个物品加上一个附加权值(-k),然后求出来的最大权值(f'(x))(f(x)-kx)是等价的,于是(b_{max}=max{f'(x)}),这个式子没有数量限制,直接(DP)就行了,中间顺便记录最佳决策点((x_{max},b_{max}))。这样的话,就能算出来(f(x)=kx_{max}+b_{max})。用因为我们知道了(x_{max}),拿它跟(m)比较,就知道是该增大还是减小斜率(k),这也提示了我们可以二分斜率
    还有一个比较重要的细节,就是(b)的最佳决策点可能不止一个,也就是说当前的这条直线跟图像有多个切点,这样我们便无法得知(m)在左边还是右边了。我们可以通过一个策略来解决这个问题,就是取(x)最大的最佳决策点,最后直接把(x_{max})带入求出(f(m))就行了
    以下是帮助你取得大师之剑的代码(滑稽):

    #include <bits/stdc++.h>
    
    using namespace std;
    
    //dp+wqs二分
    //首先把问题转化为求树上k+1条不相交路径,使其权值和最大
    
    #define N 300000
    #define ll long long
    #define INF 10000000000000 //INF不能太大,也不能太小
    
    int n, k, eid, head[N+5];
    ll m;
    
    struct Edge {
      int next, to, w;
    }e[2*N+5];
    
    struct DP { //为了方便重载了运算符
      ll v;
      int cnt;
      DP operator + (DP rhs) {
        return DP{v+rhs.v, cnt+rhs.cnt};
      }
      bool operator < (DP rhs) const {
        return v < rhs.v || (v == rhs.v && cnt < rhs.cnt);
      }
    }f[3][N+5], temp;
    
    void addEdge(int u, int v, int w) {
      e[++eid].next = head[u];
      e[eid].to = v;
      e[eid].w = w;
      head[u] = eid;
    }
    
    DP Max(int u) {
      return max(f[0][u], max(f[1][u], f[2][u]));
    }
    
    DP newDP(DP &a, ll v0, int cnt0) {
      return DP{a.v+v0, a.cnt+cnt0};
    }
    
    void dp(int u, int fa) {
      f[0][u] = DP{0, 0}, f[1][u] = DP{-INF, 0}, f[2][u] = DP{-m, 1};
      int i, v, w;
      for(i = head[u]; i; i = e[i].next) {
        v = e[i].to, w = e[i].w;
        if(v == fa) continue;
        dp(v, u);
        temp = Max(v);
        f[2][u] = max(f[2][u]+temp, f[1][u]+max(newDP(f[0][v], w, 0), newDP(f[1][v], w+m, -1)));
        f[1][u] = max(f[1][u]+temp, f[0][u]+max(newDP(f[0][v], w-m, +1), newDP(f[1][v], w, 0)));
        f[0][u] = f[0][u]+temp;
      }
    }
    
    void check() {
      dp(1, 0);
    }
    
    int main() {
      scanf("%d%d", &n, &k); k++;
      for(int i = 1, x, y, z; i <= n-1; ++i) {
        scanf("%d%d%d", &x, &y, &z);
        addEdge(x, y, z), addEdge(y, x, z);
      }
      ll l = -INF, r = INF, ans; //二分斜率
      while(l <= r) {
        m = (l+r)>>1;
        check();
        if(Max(1).cnt < k) r = m-1;
        else l = m+1, ans = m;
      }
      m = ans;
      check();
      printf("%lld
    ", Max(1).v+ans*k);
      return 0;
    }
    

    再附一道例题
    CF739E. Gosha is hunting
    题解在这里

  • 相关阅读:
    Mybatis异常There is no getter for property named 'XXX' in 'class java.lang.String'
    js javascript 容错处理代码屏蔽js错误
    springmvc访问项目默认先访问后台再返回首页
    杂记-格式化Date默认格式,日期加一天,jstl判断字符类型,ajax模拟from表单后台跳转页面,jstl访问数据库并在页面显示
    测试覆盖率工具EclEmma安装与使用
    GSON使用之对特殊字符的转换的处理
    eclipse启动自己添加的tomcat时tomcat的webapps下没有项目
    接口和抽象类
    枚举
    Java异常
  • 原文地址:https://www.cnblogs.com/dummyummy/p/10574081.html
Copyright © 2011-2022 走看看