首页 > 代码库 > 线段树区间更新+离散化——ZOJ 3299

线段树区间更新+离散化——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;
}



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