李超线段树

Posted z1j1n1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了李超线段树相关的知识,希望对你有一定的参考价值。

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

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 $u\geq v >k\geq 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|+2\sqrt {|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\n",res[t]);else
            printf("%lld\n",que(1,t));
      }
    }
  }

 

以上是关于李超线段树的主要内容,如果未能解决你的问题,请参考以下文章

李超线段树

李超线段树

李超树学习记录

李超线段树

CF932F(李超线段树+dp)

[CodeChef - STREETTA] The Street 李超线段树