李超线段树

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

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));
      }
    }
  }

原文地址:https://www.cnblogs.com/zhujiangning/p/8446904.html

时间: 2024-08-03 01:51:56

李超线段树的相关文章

【BZOJ-1568】Blue Mary开公司 李超线段树 (标记可持久化)

1568: [JSOI2008]Blue Mary开公司 Time Limit: 15 Sec  Memory Limit: 162 MBSubmit: 557  Solved: 192[Submit][Status][Discuss] Description Input 第一行 :一个整数N ,表示方案和询问的总数. 接下来N行,每行开头一个单词“Query”或“Project”. 若单词为Query,则后接一个整数T,表示Blue Mary询问第T天的最大收益. 若单词为Project,则后

【BZOJ-4515】游戏 李超线段树 + 树链剖分 + 半平面交

4515: [Sdoi2016]游戏 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 304  Solved: 129[Submit][Status][Discuss] Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会选择一条从 s 到 t 的路径,在这条路径上的每一个点上都添加一个数字.对于路径上

【BZOJ4515】游戏,树链剖分+永久化标记线段树维护线段信息(李超线段树)

Time:2016.05.10 Author:xiaoyimi 转载注明出处谢谢 传送门 思路: 李超线段树 一开始听faebdc讲,并没有听的很懂ww 后来找到良心博文啊有木有 折越 首先可以把修改转换一下,因为那个dis非常不爽.显然s~t的路径有s~lca和lca~t组成.令d[x]表示x的深度,对于s~lca上面的点,修改的值相当于a*(d[s]-d[x])+b=-a*d[x]+(b-a*d[s]),lca~t上面的点的值相当于a*(d[s]+d[x]-2*d[lca])+b=a*d[x

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

大致题意: 给出两个序列A,B,A初始为负无穷,B初始为0,有三种操作 1.在A上区间[u,v]上加一个等差数列,取与原本A序列的最大值. 2.在B上区间[u,v]上加一个等差数列. 3.给出一个点X,询问A[X]+B[X]的值. 学习一个李超线段树就ojbk了,对于每次加入的等差数列,可以转化为y=a*i+b的一条线段,用李超线段树维护所有线段 所覆盖的区间即可.数据范围比较大,线段树可以动态开点,也可以离散化. 1 #include<cstdio> 2 #include<iostre

CF932F(李超线段树+dp)

CF932F(李超线段树+dp) 此题又是新玩法, 李超线段树合并优化\(dp\) 一个显然的\(\Theta(n^2)dp\): \(dp[x]\)表示从x出发到叶子节点的最小代价 \(dp[x] = \min(dp[y] + a[x] * b[y]) ~~(y \in subtree(x))\) 如果我们将\(b[y]\)看成斜率, \(dp[y]\)看成纵截距, \(a[x]\)看成横坐标, 那么问题转为了在平面上有一些直线, 选出与直线\(x = a[x]\)相交的最靠下的点吗, 李超线

李超线段树 - JSOI2008BlueMary开公司

李超线段树用来在平面内动态插入线段,求\(x=t\)直线与这些线段交点的最值 核心是维护每个区间的"最优势线段",即终点位置处最高的线段,询问室对所有包含\(t\)的区间的最优势线段计算答案,最后取\(max\) 模板题:JSOI2008BlueMary开公司 插入直线,求单点最大值 (看代码) #include<bits/stdc++.h> using namespace std; const int N=50004; #define lc (p<<1) #d

[bzoj1568]李超线段树模板题(标志永久化)

题意:要求在平面直角坐标系下维护两个操作: 1.在平面上加入一条线段.记第i条被插入的线段的标号为i. 2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号. 解题关键:注意标志的作用 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cmath> 6 #include<iostrea

bzoj3165 segment 超哥线段树

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3165 题意:动态增加线段,求出横坐标纵坐标最高的被覆盖点所在线段. 这个题要用到李超线段树(orz李超)……大概这是李超那篇论文出现后第二年的题?不管了直接介绍这一数据结构.李超线段树的目的就是查询出每个点被线段覆盖的情况,操作无非就是插入线段和查询两件事. 插入线段:首先计算出原有线段和现在线段在这个区间之内的函数值,如果新线段一直大直接修改返回,如果一直小也直接返回,否则递归修改至单点或

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include