1. 程式人生 > 實用技巧 >全球覆蓋(雜湊+思維)

全球覆蓋(雜湊+思維)

題目描述

小黑正在研發一款全球定位軟體,想用它來定位小白的座標。具體來說,地球可以看做一個 \(X \times Y\) 的網格矩陣,橫縱座標範圍分別是 \([0,X)\) 和 $ [0,Y)$ ,由於地球是球形結構,網格的上邊界和下邊界是相通的,左邊界和右邊界也是相通的。

現在小黑獲得了 \(n\) 組座標對,每組座標對含有兩個點的座標 \((x_{i,0},y_{i,0})\) \((x_{i,1},y_{i,1})\) ,表示地球上一個兩邊平行於座標軸的矩形的兩個對角頂點,而小白就在這個矩形內部。

然而,由於地球是球形結構,確定了座標對後仍然有多種可能的“矩形”(如下圖所示)。小黑想知道最多可能有多少面積的網格出現在所有“矩形”的交集之中,以方便他確定小白的位置。每個單元格的面積為 \(1\)

於是他把這個問題交給你了。

輸入格式

第一行三個正整數 \(n,X,Y\) ,含義如題目描述。

接下來 \(n\) 行,每行四個正整數 \(x_{i,0},y_{i,0},x_{i,1},y_{i,1}\),描述一組座標對。所有資料始終保證有 \(x_{i,0} < x_{i,1},y_{i,0}<y_{i,1}\)

輸出格式

一行,一個整數,表示所求的答案。

樣例

樣例輸入

2 10 7
2 1 8 6
4 2 5 4

樣例輸出

15

樣例 1 解釋

樣例中的情況和題目中圖片一致,其中第三種情況的面積最大。

資料範圍與提示

對於 \(100\%\) 的資料 \(n \leqslant 500000,2 \leqslant X,Y \leqslant 10^9,0\leqslant x_0,x_1<X,0\leqslant y_0,y_1<Y\)

分析

每個矩形可能有幾種劃分情況,每個確定好的兩個矩形端點可以據此劃分成一個類似於九宮格的東西,其中有一些就可以組成同一個矩形(因為網格圖相當與地球,可以轉過來),那麼我們就可以直接考慮按這些劃分完以後的線段進行計算,顯然只需要計算橫向和縱向的最長線段然後乘起來就好。

但是線段可能是分開的,不是連續的一整個線段,那麼我們就可以根據某個線段是否在某個矩形內部來進行討論。如果在第 \(i\) 個矩形內部,我們就可以把一個數字串的第 \(i\) 位變成 \(1\) ,否則為 \(0\) ,然後根據這個我們可以求每一段線段的雜湊值,相同的就載入一起,那麼就可以求出最大值了。

雜湊值的求出我們可以根據訪問到了哪個矩形進行加和,訪問到這個矩形的某個邊界第一次,那麼當前邊的雜湊值就加上屬於這個矩形的那個 \(1\)

所代表的雜湊值,第二次訪問就減去。需要注意的是,如果有兩個以上的邊界在同一個座標,需要直接判掉,不然會做多餘的貢獻。

程式碼

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int L = 1 << 20;
char buffer[L],*S,*T;
#define getchar() (S == T && (T = (S = buffer) + fread(buffer,1,L,stdin),S == T) ? EOF : *S++)
#define ll unsigned long long
#define int long long
inline int read(){
	int s = 0,f = 1;
	char ch = getchar();
	while(!isdigit(ch)){
		if(ch == '-')f = -1;
		ch = getchar();
	}
	while(isdigit(ch)){
		s = s * 10 + ch - '0';
		ch = getchar();
	}
	return s * f;
}//以上為快讀
const int maxn = 1e7+10;
const ll mod = 1e7+141;
ll base = 2;
long long mp1[20000100];//記錄橫向每個雜湊值的線段長度和
long long mp2[20000100];//縱向每個雜湊值的線段長度和
struct N{
	int x,y,id;
}jl[maxn << 2];//記錄每個矩形的端點座標,方便對雜湊值進行加減
int p[maxn];
int vis[maxn];//記錄當前座標被訪問幾次
bool cmp1(N a,N b){
	return a.x < b.x;
}
bool cmp2(N a,N b){
	return a.y < b.y;
}
signed main(){
	freopen("globe.in","r",stdin);
	freopen("globe.out","w",stdout);
	int n = read(),X = read(),Y = read();
	p[0] = 1;
	for(int i = 1;i <= n;++i){//處理每一位應該加上什麼雜湊值
		p[i] = p[i-1] % mod * base % mod;
	}
	p[0] = 0;
	for(int i = 1;i <= n;++i){//記錄矩形邊界
		jl[i].x = read();
		jl[i].y = read();
		jl[i+n].x = read();
		jl[i+n].y = read();
		jl[i].id = jl[i+n].id = i;
	}
	jl[n*2+1].x = X;
	jl[n*2+1].y = Y;
	sort(jl+1,jl+n*2+1,cmp1);//按橫座標排個序
	ll hsh = 0;
	long long mx1 = 0,mx2 = 0;
	if(jl[1].x != 0){//第一個要特殊處理
		mp1[0] += jl[1].x;
	}
	for(int i = 1;i <= 2 * n;++i){//找橫座標最大
		if(jl[i].x == jl[i+1].x)continue;//如果相等就直接不計算
		if(!vis[jl[i].id])hsh += p[jl[i].id],vis[jl[i].id] = 1,hsh %= mod;
		else{
			hsh -= p[jl[i].id];
			hsh %= mod;
		}
		mp1[hsh] += jl[i + 1].x - jl[i].x;
		mx1 = max(mx1,mp1[hsh]);
	}
	memset(vis,0,sizeof(vis));
	hsh = 0;
	sort(jl+1,jl+n*2+1,cmp2);//同上,此時處理縱座標
	if(jl[1].y != 0){
		mp2[0] += jl[1].y;
	}
	for(int i = 1;i <= 2*n;++i){
		if(jl[i].y == jl[i+1].y)continue;
		if(!vis[jl[i].id])hsh += p[jl[i].id],vis[jl[i].id] = 1,hsh %= mod;
		else{
			hsh -= p[jl[i].id];
			hsh %= mod;
		}
		mp2[hsh] += jl[i+1].y - jl[i].y;
		mx2 = max(mx2,mp2[hsh]);
	}
	printf("%lld\n",mx1 * 1LL * mx2);
}