题目链接: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<=R ≤ N), 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行包含四个整数X1、Y1、X2、Y2 (0 = X1)< X2 = 1000,0 = Y1 <Y2 = 1000),指示i-th矩形的左下角和右上方坐标为(X1, Y1)和 (X)2, Y2。矩形编号从 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;
}