博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[BZOJ 1568][JSOI2008]Blue Mary开公司
阅读量:5266 次
发布时间:2019-06-14

本文共 2760 字,大约阅读时间需要 9 分钟。

[JSOI2008]Blue Mary开公司

题意

\(n\) 次操作, 维护一个一次函数集合 \(S\). 有两种操作:

  • 给定 \(b\)\(k\), 向 \(S\) 中插入一个函数 \(f(x)=kx+b\).
  • 给定一个 \(x\), 求 \(\max\limits_{f\in S}\{f(x)\}\).

\(n\le 1\times 10^5,x\le 50000\).

题解

AFO前的打板子日常

讲道理为啥一个 \(\log\) 的题数据范围才给这么点啊

李超线段树板子题.

关于李超树

李超线段树是一种特殊的标记永久化线段树. 说它特殊, 是因为标记虽然永久化但是依然会有下传.

在这种线段树中, 每个结点维护的是覆盖当前结点的 "优势线段", 也即当前结点中有超过一半的 \(x\) 的查询值在这个线段处取到. 由于是一次函数, 实际上等价于当前结点的 \(mid\) 处以这个函数为最大函数值.

当插入一个函数 \(f(x)\) 的时候, 分三种情况:

  • 如果原来的优势线段 \(g(x)\) 在当前结点所在区间的所有 \(x\) 处都有 \(g(x)\le f(x)\), 那么直接把当前结点的优势线段设为 \(f(x)\) 并结束插入过程.
  • 如果 \(\forall x,f(x)\le g(x)\), 那么不进行任何修改, 结束插入过程.
  • 如果不满足上面两个条件, 那么判断 \(g(x)\)\(f(x)\) 哪个是优势线段, 并计算出它们的交点. 显然不包含交点的一半子树中 \(f(x)\)\(g(x)\) 的关系如上面两种情况, 但是被淘汰的线段仍然可能在另一个子树中的某个点中成为优势线段, 所以将被淘汰的线段递归处理.

查询的时候就像普通的标记永久化线段树一样查到叶子并用路径上的结点上存储的优势线段更新答案.

不难发现如果维护的是函数集合的话复杂度是一个 \(\log\) 的. 如果是在某一段特定区间上产生一个一次函数状的贡献的话, 会先将这个函数分布到 \(O(\log n)\) 个结点上然后下传, 这种情况下时间复杂度是 \(O(\log^2 n)\) 的.

李超树的实际表现还是很优秀的, 很多时候在区间维护的情况下也不会比普通线段树慢太多.

具体实现的时候善用 std::swap 可以帮助减少冗余代码. 而且交点不用真的去求, 因为是一次函数所以判断区间两端的函数值的大小关系就知道它们是否在这个区间内相交了.

关于这道题

这道坑爹的板子题有几个小坑点:

  • 输出的时候需要转单位.
  • 初值为 \(0\) (也就是如果唯一的函数在查询的 \(x\) 处为负数的时候应该查得 \(0\)).
  • 因为初值是 \(0\) 所以不存在向 \(0\) 取整还是向下取整的问题.

参考代码

#include 
const int MAXN=1e5+10;struct Line{ double k; double b; Line(double a,double b):k(a),b(b){} double operator()(const double& x)const{ return k*(x-1)+b; }};struct Node{ int l; int r; Line f; Node* lch; Node* rch; Node(int,int); double Query(int); void Insert(Line);};char buf[100];int main(){ int q; bool flag=false; scanf("%d",&q); Node* N=new Node(1,5e4); while(q--){ scanf("%s",buf); if(*buf=='P'){ flag=true; double k,b; scanf("%lf%lf",&b,&k); N->Insert(Line(k,b)); } else{ int x; scanf("%d",&x); if(!flag) puts("0"); else printf("%d\n",int(N->Query(x)/100)); } } return 0;}double Node::Query(int x){ if(this->l==this->r) return this->f(x); else{ if(x<=this->lch->r) return std::max(this->f(x),this->lch->Query(x)); else return std::max(this->f(x),this->rch->Query(x)); }}void Node::Insert(Line f){ int mid=(this->l+this->r)>>1; if(f(mid)>this->f(mid)) std::swap(f,this->f); double ld=this->f(this->l)-f(this->l); double rd=this->f(this->r)-f(this->r); if(ld>=0&&rd>=0) return; else if(rd>=0) this->lch->Insert(f); else this->rch->Insert(f);}Node::Node(int l,int r):l(l),r(r),f(0,0){ if(l!=r){ int mid=(l+r)>>1; this->lch=new Node(l,mid); this->rch=new Node(mid+1,r); }}

o_8429786_p0.jpg

转载于:https://www.cnblogs.com/rvalue/p/10637011.html

你可能感兴趣的文章
MYSQL 总结——1
查看>>
通过FTP无法删除文件
查看>>
Wannafly挑战赛1 C MMSet2 虚树
查看>>
Linux进程间通信方式--信号,管道,消息队列,信号量,共享内存
查看>>
PHP,JAVA,NET 开发比较
查看>>
简单的介绍自己
查看>>
一个重写的ToString()方法引发的装箱
查看>>
第一天,浅玩 Cassandra 集群配置及启动
查看>>
为自己的教育投资,老师带会更快,不然也就没有老师存在了
查看>>
划分树--区间第K元素模板
查看>>
JavaScript归并方法reduce()和reduceRight()
查看>>
js 循环BUG
查看>>
中世纪开始在英语里也用作Affrike指非洲
查看>>
九、数组以及排序和查找
查看>>
构建之法阅读心得(八)
查看>>
每次启动word 2007时都要进行安装配置的解决方法
查看>>
Hello 2018 A,B,C,D
查看>>
gym-101343B-So You Think You Can Count?
查看>>
每周总结15
查看>>
OpenCV_用鼠标在窗口画方形
查看>>