zoukankan      html  css  js  c++  java
  • 李超线段树

    李超线段树可以维护两两间至多有一个交点的函数覆盖,单点求极值问题。

    codechef NOV17 POLY

    给定n个形如yi(x)=$a0+a1^x+a2x^2+a3x^3$的函数以及q个询问.每个询问给定整数t,你需要求出使得yi(t)最小化的函数yi。

    Lemma: Polynomial $y=x^3+ax^2+bx+c$ has at most one root greater than $k=sqrt{max(|b|,|c|)}+2$.

    Proof: Let $ugeq v >kgeq w$ to be the roots of $y$. Then $y=(x-u)(x-v)(x-w)$ so $b=uv+uw+vw$, $c=-uvw$. Since $u,v>sqrt {|c|}$ it holds that $|w|<1$. Thus $b=uv+w(u+v)> uv-(u+v)=(u-1)(v-1)-1$. But since $u,v>sqrt {|b|} + 2$ we have $b> (sqrt {|b|} + 1)^2-1=|b|+2sqrt {|b|}>b$ which is contradiction.

     于是在t>=350的情况下,三次函数最多有一个交,用李超线段树维护即可

    #include <bits/stdc++.h>
    #define LL long long 
    using namespace std;
    
      int cnt,n,Q;
      LL f[100001][4],res[1001];
    
      LL calc(LL tar,int po){
          return(f[po][0]+f[po][1]*tar+f[po][2]*tar*tar+f[po][3]*tar*tar*tar);
      }
     
      struct treenode{
          int lc,rc,l,r,lab;
      }tr[3000001];
    
      void build(int l,int r){
          tr[++cnt].l=l;tr[cnt].r=r;tr[cnt].lab=1;
          if (l==r) return;
          
          int mid=(l+r)>>1,t=cnt;
          tr[t].lc=cnt+1;
          build(l,mid);
          tr[t].rc=cnt+1;
          build(mid+1,r);
      }
    
      void ins(int po,int num){
          if (tr[po].l==tr[po].r){
            if (calc(tr[po].l,num)<calc(tr[po].l,tr[po].lab))    
              tr[po].lab=num;
            return;
        }
        
        int mid=(tr[po].l+tr[po].r)>>1,l=tr[po].l,r=tr[po].r;
        if (calc(mid,num)<calc(mid,tr[po].lab)){
          int t=tr[po].lab;tr[po].lab=num;
          if (calc(l,num)>calc(l,t))    
            ins(tr[po].lc,t);
          if (calc(r,num)>calc(r,t))
            ins(tr[po].rc,t);
        }else{
          if (calc(l,num)<calc(l,tr[po].lab))    
            ins(tr[po].lc,num);
          if (calc(r,num)<calc(r,tr[po].lab))
            ins(tr[po].rc,num);
        }
      }
    
      LL que(int po,int tar){
          if (tr[po].l==tr[po].r)
            return(calc(tr[po].l,tr[po].lab));
          
          LL ret=calc(tar,tr[po].lab);
          int mid=(tr[po].l+tr[po].r)>>1;
          if (tar<=mid)
            ret=min(ret,que(tr[po].lc,tar));else
            ret=min(ret,que(tr[po].rc,tar));
          return(ret);
      } 
    
      int main(){      
          int T;
          scanf("%d",&T);
          while (T--){
            scanf("%d",&n);
            for (int i=1;i<=n;i++)
            scanf("%lld%lld%lld%lld",&f[i][0],&f[i][1],&f[i][2],&f[i][3]);
          for (int i=1;i<=350;i++){
              res[i]=calc(i,1);
              for (int j=2;j<=n;j++)
                res[i]=min(res[i],calc(i,j));
          }
          
          for (int i=1;i<=cnt;i++) tr[i].lc=tr[i].rc=tr[i].lab=0;
          cnt=0;
          build(351,100000);
          for (int i=2;i<=n;i++)
            ins(1,i);
          scanf("%d",&Q);
          while (Q--){
              int t;
              scanf("%d",&t);
              if (t<=350) printf("%lld
    ",res[t]);else
                printf("%lld
    ",que(1,t));
          }
        }
      }

    -------------------------------------------------------------------------

    codechef MAY17 KILLER

    观察发现贡献的式子是关于dx的二次函数,但是两个二次函数做差所得的函数的二次系数与一次系数符号相同。于是两个函数最多只有一个正的根,可以用李超线段树维护。

    在树形的情况下dp转移的常数项会包含一条链上与儿子的dp值的和。

    可以发现在合并时相当于将每个儿子的函数值加上一个常数,可以启发式合并。

    复杂度$mathcal{O}(n*log^2{}{n})$

  • 相关阅读:
    TP5手动引入PHPEXCEL的方法
    分库分表的基本思想
    FIFO、LRU、LFU的含义和原理
    Memcache 内存分配策略和性能(使用)状态检查
    php soapclient 超时 设置
    jsonrpc.js -- 原生js实现 JSON-RPC 协议
    前端编译原理 笔记 -- BISON
    前端编译原理 移进规约自动机
    前端编译原理 parser.js源码解读
    react-rotuer动态加载模块
  • 原文地址:https://www.cnblogs.com/zhujiangning/p/8446904.html
Copyright © 2011-2022 走看看