poj3695——静态线段树+矩形面积并

题目链接:http://poj.org/problem?id=3695

You are developing a software for painting rectangles on the screen. The software supports drawing several rectangles and filling some of them with a color different from the color of the background. You are to implement an important function. The function answer such queries as what is the colored area if a subset of rectangles on the screen are filled.

Input

The input consists of multiple test cases. Each test case starts with a line containing two integers N(1 ≤ N ≤ 20) and M(1 ≤ M ≤ 100000), indicating the number of rectangles on the screen and the number of queries, respectively.
The i-th line of the following N lines contains four integers X1,Y1,X2,Y2 (0 ≤ X1 < X2 ≤ 1000, 0 ≤ Y1 < Y2 ≤ 1000), which indicate that the lower-left and upper-right coordinates of the i-th rectangle are (X1, Y1) and (X2, Y2). Rectangles are numbered from 1 to N.
The last M lines of each test case describe M queries. Each query starts with a integer R(1<=RN), which is the number of rectangles the query is supposed to fill. The following list of R integers in the same line gives the rectangles the query is supposed to fill, each integer of which will be between 1 and N, inclusive.

The last test case is followed by a line containing two zeros.

Output

For each test case, print a line containing the test case number( beginning with 1).
For each query in the input, print a line containing the query number (beginning with 1) followed by the corresponding answer for the query. Print a blank line after the output for each test case.

Sample Input

2  2
0 0 2 2
1 1 3 3
1 1
2 1 2
2 1
0 1 1 2
2 1 3 2
2 1 2
0 0

Sample Output

Case 1:
Query 1: 4
Query 2: 7

Case 2:
Query 1: 2

题目翻译:

您正在开发一个用于在屏幕上绘制矩形的软件。该软件支持绘制多个矩形,并填充其中一些矩形的颜色不同于背景的颜色。您将实现一个重要函数。如果填充了屏幕上的矩形子集,该函数将回答诸如彩色区域等查询。‎

‎输入‎

‎输入由多个测试用例组成。每个测试用例以包含两个整数‎‎N‎‎(1 = ‎‎N‎‎ = 20) 和‎‎M‎‎(1 = ‎‎M‎‎ = 100000) 的行开始,分别指示屏幕上的矩形数和查询数。‎
‎以下‎‎N‎‎行的‎‎i-th‎‎行包含四个整数‎‎X‎‎1、Y‎‎1、X‎‎2、Y‎‎2‎‎ (0 = ‎‎X‎‎1)‎‎< ‎‎X‎‎2‎‎ = 1000,0 = Y1 <Y2 = 1000),指示‎‎i-th‎‎矩形的左下角和右上方坐标为‎‎(X‎‎1,‎‎ Y‎‎1)‎‎和 (‎‎X)‎‎2‎‎,‎‎ Y‎‎2‎‎。矩形编号从 1 到‎‎N‎‎。‎
‎每个测试用例的最后‎‎M‎‎行描述‎‎M‎‎查询。每个查询都以整数‎‎R‎‎(1_lt;=‎‎R‎‎ = ‎‎N‎‎) 开头,这是查询应填充的矩形数。同一行中的‎‎R‎‎整数列表给出了查询应填充的矩形,每个整数介于 1 和‎‎N‎‎之间,包括 。‎

‎最后一个测试用例后跟一行包含两个零。‎

‎输出‎

‎对于每个测试用例,打印一行包含测试用例号(以 1 开头)。‎
‎对于输入中的每个查询,打印一行包含查询编号(以 1 开头),后跟查询的相应答案。在每个测试用例的输出后打印一张空白行。

 

题意理解

给你一些矩形,编号为1~n。有m次询问,每次问你其中的一些矩形的面积并。

这个题不难想到直接的做法,每次查询的时候就建树,求出面积,但是这样时间复杂度太高。

比较经典的线段树做法是建立静态线段树,然后查的的时候再取就行了。

下面附上这个题的技巧:

1 TLE的话应该是没离散化,这题必须离散化,原以为最长1000的线段可以不离散化,

可是最多有20个矩形那最多就有40个线段, 100000*log40和100000*log1000时间肯定是不一样...

2 只建立一次线段树..不要问一次建一次 因为加入的线段过后肯定会被删除

3 最好只开始的时候对线段排次序,然后开个mark[]数组,记录哪几个矩形的线段是此次询问要选的,不要每次询问都对线段排序.

4 别用G++交...G++比C++平均慢了500MS 这题就卡了那么点时间

5 前边的优化如果都做了的话..应该就过了

还有最坑的,用的double读的话会超时,用int就不会,这是什么鬼QAQ!

线段树做法(简单的做法是容斥定理做):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lson rt<<1,l,m
#define rson rt<<1|1,m+1,r
using namespace std;
const int maxn=205;
struct node{
	int l,r,h;
	int d,id;
	node(){}
	node(int l,int r,int h,int d,int id):l(l),r(r),h(h),d(d),id(id){}
	bool operator <(const node& a)const{
		return h<a.h;
	}
}Line[maxn];
int sum[maxn<<2];
int cnt[maxn<<2];
int x[maxn];
void push_up(int rt,int l,int r){
	if(cnt[rt]) sum[rt]=x[r+1]-x[l];
	else if(l==r) sum[rt]=0;
	else sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void update(int L,int R,int v,int rt,int l,int r){
	if(L<=l&&R>=r){
		cnt[rt]+=v;
		push_up(rt,l,r);
		return ;
	}
	int m=(l+r)>>1;
	if(L<=m) update(L,R,v,lson);
	if(m<R) update(L,R,v,rson);
	push_up(rt,l,r);
}
struct Rectangle{
	int x1,y1,x2,y2;
}p[maxn];
struct Point{
	int x;
	int id;
	bool operator < (const Point& others) const{
		return x<others.x;
	}
}px[maxn];
int visited[maxn]; 
int main(){
	int kcase=0;
	int N,M,q;
	while(~scanf("%d%d",&N,&M)){
		if(!N&&!M) break;
		memset(cnt,0,sizeof cnt);
		memset(sum,0,sizeof sum);
		int ans=0; 
		for(int i=1;i<=N;i++){
			scanf("%d%d%d%d",&p[i].x1,&p[i].y1,&p[i].x2,&p[i].y2);
			int x1=p[i].x1;
			int x2=p[i].x2;
			int y1=p[i].y1;
			int y2=p[i].y2;
			px[ans].x=x1;
			px[ans].id=i;
			Line[ans++]=node(x1,x2,y1,1,i);
			px[ans].x=x2;
			px[ans].id=i;
			Line[ans++]=node(x1,x2,y2,-1,i);
		}
		sort(px,px+ans);
		sort(Line,Line+ans);	
		printf("Case %d:\n",++kcase);
		int dd=1;
		for(int i=1;i<ans;i++)
			if(px[i].x!=px[i-1].x)
				px[dd++]=px[i];
		for(int i=1;i<=M;i++){
			memset(visited,0,sizeof visited);
			scanf("%d",&q);
			while(q--){
				int xx;
				scanf("%d",&xx);
				visited[xx]=1;
			}
			int k=0;
			for(int j=0;j<dd;j++){
				if(!visited[px[j].id]) continue;
				x[k++]=px[j].x;
			}
			int res=0;
			int pre=0;
			int preh=-1;
			for(int j=0;j<ans;j++){
				if(!visited[Line[j].id]) continue;
				int l=lower_bound(x,x+k-1,Line[j].l)-x;
				int r=lower_bound(x,x+k-1,Line[j].r)-x-1;
				update(l,r,Line[j].d,1,0,k-1);
				if(preh>=0)
					res+=pre*(Line[j].h-preh);
				pre=sum[1];
				preh=Line[j].h; 
			}
			printf("Query %d: %d\n",i,res);
		}
		printf("\n");
	}
	return 0;
}

容斥定理+矩形分割做法:

求两个矩形的并的时候可以使用条件

x1=max(p.x1,q.x1);
y1=max(p.y1,q.y1);
x2=min(p.x2,q.x2);
y2=min(p.y2,q.y2);

而if(x2>x1&&y2>y1)可以并,否则,并不了

同理可以得出三个的。。。。自己画个图推一下比较好。

#include<iostream>
#include<cstring>
using namespace std;
int n,m,q;
int X1[105],Y1[105],X2[105],Y2[105];
int a[105],sum[105];
void Cover(int x1,int y1,int x2,int y2,int k,int c){
	while(k<=q&&(x1>X2[a[k]]||x2<X1[a[k]]||y1>Y2[a[k]]||y2<Y1[a[k]])) k++;
	if(k>q){
		sum[c]+=(x2-x1)*(y2-y1);
		return ;
	}
	if(x1<X1[a[k]]){
		Cover(x1,y1,X1[a[k]],y2,k+1,c);
		x1=X1[a[k]];
	}
	if(x2>X2[a[k]]){
		Cover(X2[a[k]],y1,x2,y2,k+1,c);
		x2=X2[a[k]];
	}
	if(y1<Y1[a[k]]){
		Cover(x1,y1,x2,Y1[a[k]],k+1,c);
		y1=Y1[a[k]];
	}
	if(y2>Y2[a[k]]){
		Cover(x1,Y2[a[k]],x2,y2,k+1,c);
		y2=Y2[a[k]];
	}
} 
int main(){
	int kcase=0;
	while(~scanf("%d%d",&n,&m)){
		if(!n&&!m) break;
		printf("Case %d:\n",++kcase); 
		for(int i=1;i<=n;i++)
			scanf("%d%d%d%d",&X1[i],&Y1[i],&X2[i],&Y2[i]);
		for(int i=1;i<=m;i++){
			scanf("%d",&q);
			for(int j=1;j<=q;j++)
				scanf("%d",&a[j]);
			memset(sum,0,sizeof sum);
			for(int j=q;j>=1;j--)
				Cover(X1[a[j]],Y1[a[j]],X2[a[j]],Y2[a[j]],j+1,a[j]); 
			long long res=0;
			for(int j=1;j<=q;j++)
				res+=sum[a[j]];
			printf("Query %d: %lld\n",i,res);
		}
		printf("\n");
	}
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值