HDU 4533 威威貓系列故事——曬被子

威威貓系列故事——曬被子

Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 230    Accepted Submission(s): 45


Problem Description
  因爲馬拉松初賽中吃雞腿的題目讓不少人抱憾而歸,威威貓一直覺得愧對大家,這幾天他悄悄搬到直角座標系裏去住了。
  生活還要繼續,太陽也照常升起,今天,威威貓在第一象限曬了N條矩形的被子,被子的每條邊都和座標軸平行,不同被子的某些部分可能會疊在一起。這時候,在原點處突然發了場洪水,時間t的時候,洪水會蔓延到( t, t ),即左下角爲( 0, 0 ) ,右上角爲( t, t )的矩形內都有水。
  悲劇的威威貓想知道,在時間t1, t2, t3 ... tx 的時候,他有多少面積的被子是溼的?
 

Input
輸入數據首先包含一個正整數T,表示有T組測試數據;
每組數據的第一行首先是一個整數N,表示有N條被子;
接下來N行,每行包含四個整數x1, y1, x2, y2,代表一條被子的左下角和右上角的座標;
然後接下來一行輸入一個整數x,表示有x次詢問;
再接下來x行,輸入x個嚴格單調遞增的整數,每行一個,表示威威貓想知道的時間ti。

[Technical Specification]
T <= 5
0 < N <= 20000
1 <= x1 < x2 <= 200000
1 <= y1 < y2 <= 200000
1 <= x <= 20000
1 <= ti <= 200000 (1 <= i <= x )
 

Output
對於每次詢問,請計算並輸出ti時有多少面積的被子是溼的,每個輸出佔一行。
 

Sample Input
1 2 1 1 3 3 2 2 4 4 5 1 2 3 4 5
 

Sample Output
0 1 5 8 8
 

Source
 

Recommend
liuyiding
 

題目大意:

在第一象限中給出若干矩形(點範圍1e5,矩形個數20000),現在給出一些詢問(次數20000),每次詢問給出一個整數t,問在(0,0)到(t,t)範圍的矩形面積和。

解題思路:

考慮每次詢問t,對於單一矩形的面積的計算方法~


對於詢問t。計算如圖矩形所被包含的面積可以用矩形面積S[TCFI]-S[TJGI],而S[TCFI]=(t-Fx)*(t-Fy);S[TJGI]=(t-Gx)*(t-Gy)
換句話說就是用[T和矩形左下角的點形成的面積]減去[T和矩形右下角形成的矩形面積]就是這個矩形被包含的面積!

下面來看一個類似的情況:


對於這次詢問t。當前矩形被包涵的面積是S[TLFI]-S[TLEK]。即[T和矩形左下角點形成的面積]減去[T和矩形左上角點形成的矩形的面積]

那麼對於矩形被包含進(t,t)範圍是什麼情況呢?


這時候的面積是EHGF的面積,但我們還想計算這個面積時和T有關。仿照前面的討論,發現S[EHGF]不就是S[TLFI]-S[TLEN]-S[TMGI]+S[TMHN]麼?
換句話描述,就是[T和矩形左下角點形成的矩形面積]減去[T和矩形左上角點形成的矩形面積]減去[T和矩形右下角點形成的矩形面積]加上[T和矩形右上角點形成的矩形面積]

那麼我們得到了如下算法:
輸入詢問t
sum=0
遍歷所有矩形的四個頂點
   如果該頂點在(0,0)-(t,t)的範圍內
      如果當前頂點是它所在矩形的左上角或右下角的點那麼sum+=[(t,t)和該點形成的矩形的面積]
      否則sum-=[(t,t)和該點形成的矩形的面積]
返回sum

對於這題目的數據來說時間複雜度肯定是不夠的,我們要想辦法優化它。。。

觀察我們計算[T和當前點形成的矩形面積]時的方法:
假設當前點座標是(x,y)
那麼S=(t-x)*(t-y)
我們可以將上式展開:S=t*t-t(x+y)+xy
我們可不可以將上式分成的三部分分別求和呢?答案是可以的!

那麼我們可以將所有矩形左下角和右上角的點分到一組a(因爲它們和T形成的矩形面積都是做“加”運算),把左上角和右下角的點分到一組b(因爲它們和T形成的矩形面積都是做“減”運算)

那麼結果可以寫成sigma[a中在(t,t)範圍內的點和T形成的矩形面積]-sigma[b在(t,t)範圍內的點和T形成的矩形面積]

很容易想到,我們將a,b中的點分別按max(x,y)排序。然後正確的算法已經呼之欲出了!
對於每次詢問t,我們二分找到它在a,b中的位置n,m(即max(x,y)恰好不超過t的最大的下標,a,b都是從1開始編號)
答案不就是
Sum(Sa)-Sum(Sb)
=sigma[t*t-t*(x+y)+xy](a中點)-sigma[t*t-t*(x+y)+xy](b中點)
=[sigma(t*t)-sigma(x+y)+sigma(xy)](a中點)-[sigma(t*t)-sigma(x+y)+sigma(xy)](b中點)

計算sigma(t*t)只要t*t乘個數(對於a是n,對於b是m)即可!
計算sigma(x+y)和sigma(xy)只要預處理一下即可!

現在算法如下:

檢查所有矩形的四個頂點
        如果是左下角或是右上角的點那麼放到a的末尾
        否則放到b的末尾
將a,b中的所有點按max(x,y)排序
定義suma,sumb表示a、b的點中下標1到下標i的所有點的x+y和
定義suma_mul,sumb_mul表示a、b的點中下標1到下標i的所有點的x*y和
循環 i=1 到 2*N 
        suma[i]=suma[i-1]+a[i].x+a[i].y
        sumb[i]=sumb[i-1]+b[i].x+b[i].y
        suma_mul[i]=suma_mul[i-1]+a[i].x*a[i].y
        sumb_mul[i]=sumb_mul[i-1]+b[i].y*b[i].y
對於每次詢問t
        二分找到在a,b中max(x,y)恰好不超過t的下標n,m
        輸出答案(t*t*n-t*suma[n]+suma_mum[n])-(t*t*m-t*sumb[m]+sumb_mul[m])

另外:由於本題的詢問範圍是固定且是遞增的,所以可以考慮在這裏再次優化時間複雜度

代碼:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
struct point{
	long long x,y;
	friend bool operator < (const point &a,const point &b){
		return max(a.x,a.y)<max(b.x,b.y);
	}
};
point a[50001];
long long suma[50001],sumb[50001],suma_mul[50001],sumb_mul[50001];
point b[50001];
int find_a(int l,int r,long long t){
	while(r>=l){
		int m=(l+r)/2;
		if(t==max(a[m].x,a[m].y))return m;
		if(t<max(a[m].x,a[m].y))r=m-1;
		else l=m+1;
	}
	return l;
}
int find_b(int l,int r,long long t){
	while(r>=l){
		int m=(l+r)/2;
		if(t==max(b[m].x,b[m].y))return m;
		if(t<max(b[m].x,b[m].y))r=m-1;
		else l=m+1;
	}
	return l;
}
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		memset(suma,0,sizeof(suma));
		memset(sumb,0,sizeof(sumb));
		memset(suma_mul,0,sizeof(suma_mul));
		memset(sumb_mul,0,sizeof(sumb_mul));
		memset(a,0,sizeof(a));
		memset(b,0,sizeof(b));
		int n;
		scanf("%d",&n);
		for(int i=1;i<=n;i++){
			long long x1,y1,x2,y2;
			scanf("%I64d%I64d%I64d%I64d",&x1,&y1,&x2,&y2);
			//cin>>x1>>y1>>x2>>y2;
			a[2*i-1]=(point){x1,y1};
			a[2*i]=(point){x2,y2};
			b[2*i-1]=(point){x1,y2};
			b[2*i]=(point){x2,y1};
		}
		sort(a,a+2*n+1);
		sort(b,b+2*n+1);
		for(int i=1;i<=2*n;i++){
			suma[i]=suma[i-1]+a[i].x+a[i].y;
			sumb[i]=sumb[i-1]+b[i].x+b[i].y;
			suma_mul[i]=suma_mul[i-1]+a[i].x*a[i].y;
			sumb_mul[i]=sumb_mul[i-1]+b[i].x*b[i].y;
		}
		suma[2*n+1]=suma[2*n];
		sumb[2*n+1]=sumb[2*n];
		suma_mul[2*n+1]=suma_mul[2*n];
		sumb_mul[2*n+1]=sumb_mul[2*n];
		int q;
		scanf("%d",&q);
		while(q--){
			long long x;
			scanf("%I64d",&x);
			long long sum=0;
			int m=find_a(1,2*n,x);
			if(m>2*n)m=2*n;
			if(x<max(a[m].x,a[m].y))m--;
			sum=m*x*x-x*suma[m]+suma_mul[m];
			m=find_b(1,2*n,x);
			if(m>2*n)m=2*n;
			if(x<max(b[m].x,b[m].y))m--;
			sum=sum-(m*x*x-x*sumb[m]+sumb_mul[m]);
			printf("%I64d\n",sum);
			//cout<<sum<<endl;
		}
	}
}



發佈了46 篇原創文章 · 獲贊 4 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章