线段树区间更新+离散化——ZOJ 3299

对应ZOJ题目:点击打开链接

Fall the Brick

Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu

Submit Status

Description

Now the God is very angry, so he wants to punish the lazy, greedy humans. He chooses to throw some lines of bricks (just down from the very high Heaven). These days the God lives in a 2D world, so he just throw the bricks in a vertical plane.
Each time, the God throws a line of bricks. The width of each brick is 1, and the length will be given.

t__nt is a hero in the world and he is trying his best to save the world. Now he has made mhorizontal boards in the air with his magic power to stop the bricks. If one brick falls onto a board, it can not fall down any more. Notice
that, for a line of bricks, consecutive bricks are not connected. So when some bricks touch a board, the others will continues to fall down.
 Now, t__nt wants to know how many bricks each board holds after the God‘s crazy action. He asks you, an ACMer,
to help him.

Input

There are no more then 10 cases. There is a blank line between consecutive cases. The first line of each case contains two integers nm (0 <nm <= 100000), indicating the number of lines of bricks and number of horizontal
boards made by t__nt. n lines follow, each contains two integers liri (0 <= li < ri <= 30000000). li and ri is the x-coordinates
for the left side and the right side of the line of bricks. m lines follow, each contains three integers aibi, and hi (0 <= ai < bi <= 30000000;
0 < hi < 1000000000), which means that board i is at height hi and the extreme points are ai and bi. You may assume no two boards with same height will overlap with each other.

Output

For each case, print m lines. The ith line means the number of bricks on board i at last. Print a blank line after each case.

Sample Input

1 2
1 8
1 5 8
3 7 6

Sample Output

4
2

Source

题意:有n排板砖,m个木板,边界的l和r的n列板砖从天上掉下来,然后有m个边界为a,b的高度为h的木板去接那些板砖,一排板砖中的部分板砖如果掉到木板上就停止下落,剩下的继续下落,问最后每块木板上有多少块板砖。

思路:数据太大,必须先离散化;所以要用左闭右开的线段树~之后把木板按高度递增排序,用线段树定下木板最终状态。比如样例(没有离散化时):[1,5-1](编号为1),盖在[3,7-1](编号为2)上就是

1 2 3 4 5 6 7 8 9

0 0 2 2 2 2 0 0 0

1 1 1 1 2 2 0 0 0

之后掉砖块就是相当于区间更新。最后把在各编号内的砖块都分别加起来就行了,要用个ID数组存编号。。。

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
#define ms(x,y) memset(x,y,sizeof(x))
const int MAXN=400100;//开始是开200100的,RE,一直开到400100,不懂~。。。
const int INF=1<<30;
using namespace std;
int cover[MAXN*6];//这里也是~,弄到*6才AC
int brick[MAXN*6];
int dis[MAXN];//离散化后的数据
int indis[MAXN];//离散化后插点后的数据
int ll[MAXN/2];
int rr[MAXN/2];
long long ID[MAXN/2];//存顺序号

struct Board
{
	int l,r,id,h;
}board[MAXN/2];

bool cmp(Board b1, Board b2)
{
	return b1.h < b2.h;
}

void down(int rt)
{
	if(cover[rt])
	{
		cover[rt<<1] = cover[rt<<1|1] = cover[rt];
		cover[rt]=0;
	}
	if(brick[rt]){//这里的传递是+=,不是赋值,好久才发现。。。
		brick[rt<<1] += brick[rt];
		brick[rt<<1|1] += brick[rt];
		brick[rt]=0;
	}
}

//定下板的最终状态
void Insertboard(int rt, int left, int right, int l, int r, int id)
{
	//必须要用左闭右开区间的方式,因为数据离散化了。。。
	if(l == left && right-1 == r){
		cover[rt] = id;
		return;
	}
	down(rt);
	int mid = (left + right)>>1;
	if(mid > r) Insertboard(rt<<1, left, mid, l, r, id);
	else if(mid <= l) Insertboard(rt<<1|1, mid, right, l, r, id);
	else{
		Insertboard(rt<<1, left, mid, l, mid-1, id);
		Insertboard(rt<<1|1, mid, right, mid, r, id);
	}
}

//掉砖块
void updata(int rt, int left, int right, int l, int r)
{
	if(l <= left && right-1 <= r){
		brick[rt]++;
		return;
	}
	if(right - left == 1) return;
	down(rt);
	int mid = (left + right)>>1;
	if(mid > r) updata(rt<<1, left, mid, l, r);
	else if(mid <= l) updata(rt<<1|1, mid, right, l, r);
	else{
		updata(rt<<1, left, mid, l, mid-1);
		updata(rt<<1|1, mid, right, mid, r);
	}
}

//数砖块
void query(int rt, int left, int right)
{
	if(cover[rt] && brick[rt]){//把完全在板上的砖加上
		ID[cover[rt]] += (long long)brick[rt]*(indis[right] - indis[left]);
		return;
	}
	if(right - left == 1) return;
	down(rt);
	int mid = (left + right)>>1;
	query(rt<<1, left, mid);
	query(rt<<1|1, mid, right);
}

int binary(int x, int u)
{
	int left = 0;
	int right = u-1;
	while(left <= right)
	{
		int mid = (left + right)>>1;
		if(indis[mid] == x) return mid;
		if(indis[mid] > x) right = mid-1;
		else left = mid+1;
	}
	return left;
}

void Input(int const n, int const m, int &u)
{
	for(int i=0; i<n; i++){
		scanf("%d%d", &ll[i],&rr[i]);
		rr[i]--;//线段树记的是点,比如如果在[1,3]掉砖块,那实际上是在1-2和2-3有两块砖,即是把右边界-1就可以了
		dis[u++]=ll[i];
		dis[u++]=rr[i];
	}
	for(int i=0; i<m; i++){
		scanf("%d%d%d", &board[i].l,&board[i].r,&board[i].h);
		board[i].r--;
		board[i].id=i+1;
		dis[u++]=board[i].l;
		dis[u++]=board[i].r;
	}
	sort(board, board + m, cmp);
}

void Initialize()
{
	ms(cover,0);
	ms(brick,0);
	ms(ID,0);
}

void Discretiza(int &u)
{
	sort(dis, dis + u);
	int k=1;
	for(int i=1; i<u; i++) if(dis[i] != dis[i-1]) dis[k++]=dis[i];
	u=1;
	indis[0] = dis[0];
	for(int i=1; i<k; i++)//离散化后的数据插点
		if(dis[i] - dis[i-1] > 1){
			indis[u++] = dis[i-1] + 1;
			indis[u++] = dis[i];
		}
		else indis[u++] = dis[i];
	indis[u] = indis[u-1]+1;//题目需要;在最后一个元素后面插入一个比它大1的值;
}

void Solve(int const n, int const m, int const u)
{
	for(int i=0; i<m; i++){
		int l = binary(board[i].l, u);
		int r = binary(board[i].r, u);
		Insertboard(1, 0, u, l, r, board[i].id);
	}
	for(int i=0; i<n; i++){
		int l = binary(ll[i], u);
		int r = binary(rr[i], u);
		updata(1, 0, u, l, r);
	}
	query(1,0,u);
}

void Print(int const m)
{
	for(int i=1; i<=m; i++) printf("%lld\n", ID[i]);
	printf("\n");
}

int main()
{
	//freopen("in.txt","r",stdin);
	int n,m;
	while(~scanf("%d%d", &n,&m))
	{
		int u=0;
		Initialize();
		Input(n,m,u);
		Discretiza(u);
		Solve(n,m,u);
		Print(m);
	}
	return 0;
}
时间: 2024-10-20 09:27:21

线段树区间更新+离散化——ZOJ 3299的相关文章

POJ 2528 Mayor&#39;s posters (线段树区间更新+离散化)

题目链接:http://poj.org/problem?id=2528 给你n块木板,每块木板有起始和终点,按顺序放置,问最终能看到几块木板. 很明显的线段树区间更新问题,每次放置木板就更新区间里的值.由于l和r范围比较大,内存就不够了,所以就用离散化的技巧 比如将1 4化为1 2,范围缩小,但是不影响答案. 写了这题之后对区间更新的理解有点加深了,重点在覆盖的理解(更新左右两个孩子节点,然后值清空),还是要多做做题目. 1 #include <iostream> 2 #include <

ZOJ 1610 Count the Colors (线段树区间更新)

题目链接 题意 : 一根木棍,长8000,然后分别在不同的区间涂上不同的颜色,问你最后能够看到多少颜色,然后每个颜色有多少段,颜色大小从头到尾输出. 思路 :线段树区间更新一下,然后标记一下,最后从头输出. //ZOJ 1610 #include <cstdio> #include <cstring> #include <iostream> using namespace std ; int p[8010*4],lz[8010*4] ,hashh[8010*4],has

ZOJ 3632 Watermelon Full of Water (线段树 区间更新 + dp)

题目大意: 让每天都能吃到西瓜.最少需要花多少钱. 思路分析: dp[pos] 就表示  要让 前i天每天都有西瓜吃,最少需要花多少钱. 那么如果你买这个西瓜的话.那么这个西瓜能吃的持续时间都要更新一下. 然后再在每个西瓜的更新部分取最小的,就可以是这个点所能得到的最小值. 其实就是 dp[i] = min (dp[i] , dp[ j - k +1] + a[j]); 但是枚举前面的时候会超时,就用线段树维护. 5 1 2 3 4 5 1 2 2 2 2 给出这组数据是说,每次买西瓜的时候,都

POJ 2528 Mayor&#39;s posters(线段树区间染色+离散化或倒序更新)

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 59239   Accepted: 17157 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

poj-----(2528)Mayor&#39;s posters(线段树区间更新及区间统计+离散化)

Mayor's posters Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 43507   Accepted: 12693 Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral post

POJ 2528 Mayor&#39;s posters (离散化+线段树区间更新)

Description The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for

POJ 2777 &amp;&amp; ZOJ 1610 &amp;&amp;HDU 1698 --线段树--区间更新

直接将这3题 放一起了  今天在做线段树的东西 这3个都是区间更新的 查询方式互相不同 反正都可以放到一起吧 直接先上链接了 touch me touch me touch me 关于涉及到区间的修改 -- 区间更新的话 分为 增减 或者 修改 主要就是个 laze 标记 就是延迟更新 对于区间更新的写法 一般是有2种 其一 仔细划分到每个细小的区间    另一 粗略划分 反正 ==我的代码里会给出2种写法 看自己喜好 hdu 1 //线段树 成段更新 ---> 替换 根结点的查询 2 3 #i

线段树区间更新,区间统计+离散化 POJ 2528 Mayor&#39;s posters

题意:有一个很长的板子(10000000长),在上面贴n(n<=10000)张海报,问最后从外面能看到几张不同的海报. 因为板子有10000000长,直接建树肯定会爆,所以需要离散化处理,对于每张海报,有两个端点值,最后能看到几张海报跟他们的端点值的相对大小有关,跟绝对大小无关,所以就把所有海报的端点离散化处理,总共2n个端点,排序去重,对应p(p<=2n)个点.然后建树,因为p不超过20000,所以这样就可以接受了.区间更新时,因为我们只关心最外面海报的颜色有多少种,所以向下传递节点信息的时

ZOJ - 1610 Count the Colors(线段树区间更新,单点查询)

1.给了每条线段的颜色,存在颜色覆盖,求表面上能够看到的颜色种类以及每种颜色的段数. 2.线段树区间更新,单点查询. 但是有点细节,比如: 输入: 2 0 1 1 2 3 1 输出: 1 2 这种情况就需要处理一下,代码中把所有的左端点都+1,避免了这种情况. 3. #include<iostream> #include<stdio.h> #include<string.h> using namespace std; #define L(root) ((root) &l