[BZOJ 1568][JSOI2008]Blue Mary开公司

摘要:
[BZOJ1568][JSOI2008]BlueMary开设公司的意图是经营(n)次并维持一系列主要职能。有两个操作:给定(b)和(k),将函数(f(x)=kx+b)插入(S)。给定a(x),计算(maxlimits_{finS}{f(x)})(nleimes10^5,xle50000)。为什么一个(日志)问题的数据范围在AFO之前提供给董事会的日常推理
[BZOJ 1568][JSOI2008]Blue Mary开公司

题意

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

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

(nle 1 imes 10^5,xle 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 <bits/stdc++.h>

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

[BZOJ 1568][JSOI2008]Blue Mary开公司第1张

免责声明:文章转载自《[BZOJ 1568][JSOI2008]Blue Mary开公司》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇高性能的JavaScript,这是一个高级程序员必备的技能Don’t Use the Win32 API PostThreadMessage() to Post Messages to UI Threads(翻译)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

可持久化数组入门

# 洛谷题目链接:[可持久化数组](https://www.luogu.org/problemnew/show/P3919) 题目描述 如题,你需要维护这样的一个长度为 NN 的数组,支持如下几种操作 在某个历史版本上修改某一个位置上的值 访问某个历史版本上的某一位置的值 此外,每进行一次操作(对于操作2,即为生成一个完全一样的版本,不作任何改动),...

[题解] bzoj 3600 没有人的算数 (替罪羊树+线段树)

- 传送门 - http://www.lydsy.com/JudgeOnline/problem.php?id=3600   - 思路 - 区间操作是线段树无疑,难点在于如何考虑这些数对的大小关系. 我们考虑一下平衡树,如果在平衡树中每个节点放一个数对,我们规定中序遍历前面的数对大于后面的,那么对于任意一个新数对(x,y)插入时只需知道x,y与每个节点的数...

HDOJ 1166 敌兵布阵树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 18120Accepted Submission(s): 7877 Problem Description C国的死对头A国这段时间正在进行军事演习...

4.18 省选模拟赛 消息传递 树剖 倍增 线段树维护等比数列

把树剖和倍增 线段树的联系诠释的很完美。 题目意思:自行理解。 做法:设两个点x,y x能挡住y 且在k点处 那么至少的得到一个式子 tx+dx-dk<tx+dy-dk 从这个式子中可以发现 按tx+dx这个排序 前面的可以有可能挡住后面的 同时相等时题目中的要求小的编号优先。 我们考虑一个点挡住当前的这个点的去路 设sx表示x这个点当前不能通过的...

POJ 2482 Stars in Your Window (线段树+扫描线+区间最值,思路太妙了)

该题和 黑书 P102 采矿 类似 参考链接:http://blog.csdn.net/shiqi_614/article/details/7819232http://blog.csdn.net/tsaid/article/details/6686907http://www.cnblogs.com/372465774y/archive/2012/07/28...

[POJ1195] Mobile phones(二维树状数组)

题目链接:http://poj.org/problem?id=1195 题意:四种操作: 0:初始化一个S*S的零矩阵 1:点(x,y)是值+A 2:查询一个子矩阵里所有数的和 3:退出 线段树由于不能在两棵树之间传递标记,所以这种求和的操作非常难处理。 改学了一下而为树状数组,发现可是比二维线段树简单多了。 记得之前曾经看过zkw线段树的ppt讲稿,好像...