[cdoj843] 冰雪奇缘 (线段树+离散)
[线段树 + 离散化]
Description
艾莎女王又开始用冰雪魔法盖宫殿了。
她决定先造一堵墙,于是释放魔法让形为直角梯形的冰砖从天而降,定入冻土之中。
现在你将回答女王的询问:某段冻土上冰砖的面积。
注:多块冰砖之间会互相重叠,重叠部分要多次计算。
Input
第一行一个整数nn,表示有nn个冰砖先后定入了冻土之中。
冻土上刚开始并没有冰砖。
接下来n行,每行6个整数,x1i,h1i,x2i,h2i,li,ri
表示一次如图所示的冰砖下落,并询问在这之后,落在[li,ri][li,ri]内冰砖的总面积。
\(2≤n≤100000, −10^8≤li,ri≤10^8,\)
\(−10^8≤x1i<x2i≤10^8,0≤h1i,h2i≤10000,x2i−x1i≤10^5,\)
\(2 ≤ n ≤ 100000,−10^8≤li<ri≤10^8,−10^8≤x1i<x2i≤10^8,\)
$0≤h1i,h2i≤10000,x2i−x1i≤10^5 $

Output
输出nn行,每行输出一个浮点数,作为对该询问的回答。误差小于1e-6的回答都被当作正确回答。
Sample Input
2
1 1 3 2 -5 5
2 2 4 1 2 3
Sample Output
3.0000000
3.50000000
Hint
如图是对样例输入的解释。

重叠部分需多次计算。
这是一道比较有意义的线段树题目,线段树的每个节点主要保存的是一段区间内的面积和。
然后要想到的是,因为是区间更新区间查询,所以需要用到懒惰标记,避免超时。
想好要用到什么方法,接下来就思考具体方案。
首先,因为是梯形,所以更新是要用到左边高度和右边高度,即梯形的上底和下底,懒惰标记也需要传递这两个值。在往下传递的时候,可以把中间的一条线分两次计算,即把梯形分成矩形和三角形两个部分,第一个部分很好得,直接是矩形的上底,第二个部分可以用相似三角形来求。
需要注意的是,由于l ~ r 中存储的是l ~ r+1的值,所以左子树中存储l ~ mid+1,,右子树中存储mid+1 ~ r+1
还需要注意的一点是,题目中由于x 范围很大,需要离散化。
下面是代码
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define ls u<<1,l,mid
#define rs u<<1|1,mid+1,r
const int maxn = 1e5 + 5;
int n;
int cnt = 1;//离散化之后点的总数
int num[maxn << 2];
struct node {// 在线段树中, l ~ r维护的是l ~ r+1 的值
	double sum;
	double addl,addr;
}nod[maxn << 2 << 2];
struct que { //存储询问,因为需要先读入所有点用于离散化
	int xl,xr,hl,hr,x,y;
}q[maxn];
void pushup(int u){
	nod[u].sum = nod[u<<1].sum + nod[u<<1|1].sum;
}
void build(int u,int l,int r){
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(ls);
	build(rs);
	pushup(u);
}
void init(){// 读入询问,离散化,建树
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		scanf("%d%d%d%d%d%d",&q[i].xl,&q[i].hl
							,&q[i].xr,&q[i].hr
							,&q[i].x ,&q[i].y );
		num[cnt] = q[i].xl;cnt++;
		num[cnt] = q[i].xr;cnt++;
		num[cnt] = q[i].x;cnt++;
		num[cnt] = q[i].y;cnt++;
	}
	sort(num + 1, num + cnt);
	cnt = unique(num+1,num+cnt) - num - 1;
	build(1,1,cnt-1);
}
void pushdown(int u,int l,int r){
	if(l == r){nod[u].addl = nod[u].addr == 0;return;}
	//计算中间点的高度
	int mid = (l + r) >> 1;
	int rr = num[r+1] - num[l];
	int midd = num[mid+1] - num[l];
	double add = nod[u].addl + (nod[u].addr - nod[u].addl) * midd / rr;
	//向下传递sum值
	nod[u<<1].sum += (nod[u].addl + add) * midd / 2;
//	nod[u<<1|1].sum += (nod[u].addr + add) * (rr - midd) / 2;
	nod[u<<1|1].sum = nod[u].sum - nod[u<<1].sum;
	//向下传递add,并将当前节点add值清零
	nod[u<<1].addl += nod[u].addl; nod[u].addl = 0;
	nod[u<<1].addr += add;
	nod[u<<1|1].addr += nod[u].addr; nod[u].addr = 0;
	nod[u<<1|1].addl += add;
}
void update(int u,int l,int r,int x,int y,double hl,double hr){
	if(l == x && r+1 == y){
		nod[u].addl += hl;
		nod[u].addr += hr;
		nod[u].sum += (hl + hr) * (num[y]-num[x]) / 2;
		return;
	}
	int mid = (l + r) >> 1;
	if(nod[u].addl || nod[u].addr)pushdown(u,l,r);
	if(y <= mid+1) update(ls,x,y,hl,hr);
	else if(x >= mid+1) update(rs,x,y,hl,hr);
	else {
		int rr = num[y] - num[x];
		int midd = num[mid+1] - num[x];
		double add = hl + (hr - hl) * midd / rr;
		update(ls,x,mid+1,hl,add);
		update(rs,mid+1,y,add,hr);
	}
	pushup(u);
}
double query(int u,int l,int r,int x,int y){
	if(l == x && r + 1 == y)return nod[u].sum;
	int mid = (l + r) >> 1;
	if(nod[u].addl || nod[u].addr)pushdown(u,l,r);
	if(y <= mid+1) return query(ls,x,y);
	if(x >= mid+1)  return query(rs,x,y);
	return query(ls,x,mid+1) + query(rs,mid+1,y);
}
void work(){
	for(int i = 1;i <= n;i++){
		int x = lower_bound(num + 1,num + cnt + 1,q[i].xl) - num ;
		int y = lower_bound(num + 1,num + cnt + 1,q[i].xr) - num ;
		update(1,1,cnt-1,x,y,q[i].hl,q[i].hr);
		x = lower_bound(num + 1,num + cnt + 1,q[i].x) - num ;
		y = lower_bound(num + 1,num + cnt + 1,q[i].y) - num ;
		printf("%lf\n",query(1,1,cnt-1,x,y));
	}
}
int main(){
	init();
	work();
	return 0;
}
[cdoj843] 冰雪奇缘 (线段树+离散)的更多相关文章
- hdu1542 Atlantis(扫描线+线段树+离散)矩形相交面积
		题目链接:点击打开链接 题目描写叙述:给定一些矩形,求这些矩形的总面积.假设有重叠.仅仅算一次 解题思路:扫描线+线段树+离散(代码从上往下扫描) 代码: #include<cstdio> ... 
- sdut 2159   Ivan comes again!(2010年山东省第一届ACM大学生程序设计竞赛)   线段树+离散
		先看看上一个题: 题目大意是: 矩阵中有N个被标记的元素,然后针对每一个被标记的元素e(x,y),你要在所有被标记的元素中找到一个元素E(X,Y),使得X>x并且Y>y,如果存在多个满足条 ... 
- 51Nod 1175 区间中第K大的数 (可持久化线段树+离散)
		1175 区间中第K大的数 基准时间限制:1 秒 空间限制:131072 KB 分值: 160 难度:6级算法题 一个长度为N的整数序列,编号0 - N - 1.进行Q次查询,查询编号i至j的所有 ... 
- [POJ]  3277  .City Horizon(离散+线段树)
		来自这两篇博客的总结 http://blog.csdn.net/SunnyYoona/article/details/43938355 http://m.blog.csdn.net/blog/mr_z ... 
- poj City Horizon (线段树+二分离散)
		http://poj.org/problem?id=3277 City Horizon Time Limit: 2000MS Memory Limit: 65536K Total Submissi ... 
- (中等) POJ 2528 Mayor's posters , 离散+线段树。
		Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral electio ... 
- hdu 3333 Turing Tree 图灵树(线段树 + 二分离散)
		http://acm.hdu.edu.cn/showproblem.php?pid=3333 Turing Tree Time Limit: 6000/3000 MS (Java/Others) ... 
- poj2528(线段树+区间离散)
		题意:那个城市里要竞选市长,然后在一块墙上可以贴海报为自己拉票,每个人可以贴连续的一块区域,后来帖的可以覆盖前面的,问到最后一共可以看到多少张海报.思路:一看就知道是线段树,只是说要利用到离散化,也不 ... 
- bzoj3932--可持久化线段树
		题目大意: 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第 ... 
随机推荐
- struts文件上传(多文件)
			第01步:配置web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version= ... 
- 有关dwr推送的笔记
			想做一个web推送相关的东东,昨天搞了一天,终于把这些杂乱的配制弄清了,今天写出来方便以后记住,也方便大家看一下吧 1:引入dwr包,我用的是maven <dependency> < ... 
- oracle中的常用语句
			1:查看当前用户的缺省表空间 SELECT USERNAME, DEFAULT_TABLESPACE FROM USER_USERS; 2:查看当前用户的角色 SELECT * FROM USER_R ... 
- ASP.NET MVC 返回JsonResult序列化内容超出最大限制报错的解决办法
			在使用MVC的时候我们经常会在Controller的Action方法中返回JsonResult对象,但是有时候你如果序列化的对象太大会导致JsonResult从Controller的Action返回后 ... 
- 锋利的JQuery(一)
			释义: Ajax:Asynchronous Javascript And XML,异步的Javascript和XML 其它库: Prototype:最早 Dojo:学习曲线陡 YUI:比较丰富 Ext ... 
- APP的UI测试要点
			1.文字显示是否正确 比如与需求图片对比是否正确,无错别字 2.对齐方式是否正确 3.图片 图片显示的篇幅不要太大. 4.颜色是否正确 颜色与需求规定的是否一致 
- QTP11.00安装+破解详细教程
			一. 安装过程 首先双击setup.exe文件,选择“QuickTest Professional安装程序” 此时会查看你机子上面是否有QTP需要,但是机子上没有的组件, 跟着先安装这两个组 ... 
- JQuery ajax方法及参数
			©屋主原创,版权归 todayeeee 所有!如需转载,必须在页面明显位置给出原文链接!商业用途请 联系我! $.ajax({ type: 'GET', // 这是请求的方式 可以是GET方 ... 
- linux设备驱动归纳总结(六):3.中断的上半部和下半部——tasklet【转】
			本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(六):3.中断的上半部和下半部——tasklet x ... 
- 【python cookbook】【字符串与文本】14.字符串连接及合并
			问题:将许多小字符串合并成一个大的字符串 解决方案: 1.针对少数量的字符串:+ 2.针对大量的字符串对象的连接,更高效的方法:join() 3.更加复杂的字符串:format() >>& ... 
