1. 程式人生 > >【bzoj4605】嶗山白花蛇草水 權值線段樹套KD-tree

【bzoj4605】嶗山白花蛇草水 權值線段樹套KD-tree

復雜 接下來 efi ora 這也 多少 線段樹 會有 如果

題目描述

神犇Aleph在SDOI Round2前立了一個flag:如果進了省隊,就現場直播喝嶗山白花蛇草水。憑借著神犇Aleph的實力,他輕松地進了山東省省隊,現在便是他履行諾言的時候了。蒟蒻Bob特地為他準備了999,999,999,999,999,999瓶嶗山白花蛇草水,想要灌神犇Aleph。神犇Aleph求(跪著的)蒟蒻Bob不要灌他,由於神犇Aleph是神犇,蒟蒻Bob最終答應了他的請求,但蒟蒻Bob決定將計就計,也讓神犇Aleph回答一些問題。具體說來,蒟蒻Bob會在一個寬敞的廣場上放置一些嶗山白花蛇草水(可視為二維平面上的一些整點),然後詢問神犇Aleph在矩形區域(x1, y1), (x2, y2)(x1≤x2且y1≤y2,包括邊界)中,嶗山白花蛇草水瓶數第k多的是多少。為了避免麻煩,蒟蒻Bob不會在同一個位置放置兩次或兩次以上的嶗山白花蛇草水,但蒟蒻Bob想為難一下神犇Aleph,希望他能在每次詢問時立刻回答出答案。神犇Aleph不屑於做這種問題,所以把這個問題交給了你。

輸入

輸入的第一行為兩個正整數N, Q,表示橫縱坐標的範圍和蒟蒻Bob的操作次數(包括放置次數和詢問次數)。 接下來Q行,每行代表蒟蒻Bob的一個操作,操作格式如下: 首先第一個數字type,表示操作種類。type=1表示放置,type=2表示詢問。 若type=1,接下來會有三個正整數x, y, v,表示在坐標整點(x, y)放置v瓶嶗山白花蛇草水。(1≤x, y≤N, 1≤v≤10^9) 若type=2,接下來會有五個正整數x1, y1, x2, y2, k,表示詢問矩形區域(x1, y1), (x2, y2)中,嶗山白花蛇草水瓶數第k多的是多少。 (1≤x1≤x2≤N,1≤y1≤y2≤N,1≤k≤Q) 為了體現程序的在線性,你需要將每次讀入的數據(除了type值)都異或lastans,其中lastans表示上次詢問的答案。如果上次詢問的答案為"NAIVE!ORZzyz."(見樣例輸出),則將lastans置為0。初始時的lastans為0。 初始時平面上不存在嶗山白花蛇草水。 本題共有12組測試數據。對於所有的數據,N≤500,000。 Q的範圍見下表: 測試點1-2 Q=1,000 測試點3-7 Q=50,000 測試點8-12 Q=100,000

輸出

對於每個詢問(type=2的操作),回答嶗山白花蛇草水瓶數第k多的是多少。若不存在第k多的瓶數, 請輸出"NAIVE!ORZzyz."(輸出不含雙引號)。

樣例輸入

10 7
1 1 1 1
1 2 2 3
1 4 1 2
1 3 4 4
2 1 1 4 1 3
2 2 2 3 5 4
2 2 1 4 4 2

樣例輸出

NAIVE!ORZzyz.
NAIVE!ORZzyz.
3


題解

權值線段樹套KD-tree

一開始想到了一個$O(n\sqrt n\log^2n)$的KD-tree套平衡樹套二分的傻*做法本以為數據水能卡過去,結果自己做的隨機極限數據跑了60s++。。

看來以後是再也不能過於相信數據結構了。

本題正解是權值線段樹/替罪羊樹套KD-tree,由於蒟蒻沒有學替罪羊樹就寫了動態開點權值線段樹。

對於外層權值線段樹的每個節點,對應著一棵KD-tree,存儲權值在外層節點範圍內的所有點。

對於每次詢問,如果判定有解,則尋找區間右半部分中點的個數,如果大於等於k則在右區間中找,否則在左區間中找,直至l=r得到答案。

嗯,說起來真是容易。然而這樣交上去肯定是必T無疑。

究其原因就是在KD-tree上。(警察叔叔,就是他!)

一開始把“平衡”KD-tree的時間復雜度當成$O(\log n)$的了(這也是我一開始想出那個做法的原因),以為穩過,結果卡得很死。

由於KD-tree不能旋轉什麽的,所以自然不能保證平衡。

那麽我們能做的只有對KD-tree進行重構,但是蒟蒻又不會比例重構(子樹大小超過某比例時重構,替罪羊樹的操作),所以只能固定點數重構。

然而重構又出了各種各樣的問題 = =,改了以後交上去還是TLE。

實在沒辦法,要了份數據,發現第6、7個點卡不重構的KD-tree,第8、9、10個點數據範圍非常大,這導致無論怎麽改重構時間都無法通過。

最後只能針對數據來解決問題了(逃),m=50000時是前7個點,m=100000時是後3個點,分情況就好了,最後還是勉強跑過了。

說實話真正考試如果出這種題的話80分真的是滿足了。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 3000010
using namespace std;
const int M = 1000000000;
int m , R , ls[N] , rs[N] , root[N] , ts[N] , tot , d , num , x1 , y1 , x2 , y2;
int sta[N] , top;
struct data
{
	int p[2] , mx[2] , mn[2] , si , c[2];
	bool operator<(const data a)const {return p[d] == a.p[d] ? p[d ^ 1] < a.p[d ^ 1] : p[d] < a.p[d];}
}a[N];
bool cmp(int x , int y)
{
	return a[x].p[d] == a[y].p[d] ? a[x].p[d ^ 1] < a[y].p[d ^ 1] : a[x].p[d] < a[y].p[d];
}
void pushup(int k)
{
	int l = a[k].c[0] , r = a[k].c[1];
	a[k].mx[0] = max(a[k].p[0] , max(a[l].mx[0] , a[r].mx[0]));
	a[k].mx[1] = max(a[k].p[1] , max(a[l].mx[1] , a[r].mx[1]));
	a[k].mn[0] = min(a[k].p[0] , min(a[l].mn[0] , a[r].mn[0]));
	a[k].mn[1] = min(a[k].p[1] , min(a[l].mn[1] , a[r].mn[1]));
	a[k].si = a[l].si + a[r].si + 1;
}
void insert(int &k)
{
	if(!k) k = ++num , a[k].p[0] = x1 , a[k].p[1] = y1;
	else if((d == 0 && (x1 == a[k].p[0] ? y1 < a[k].p[1] : x1 < a[k].p[0])) || (d == 1 && (y1 == a[k].p[1] ? x1 < a[k].p[0] : y1 < a[k].p[1]))) d ^= 1 , insert(a[k].c[0]);
	else insert(a[k].c[1]);
	pushup(k);
}
int query(int k)
{
	if(!k || x1 > a[k].mx[0] || y1 > a[k].mx[1] || x2 < a[k].mn[0] || y2 < a[k].mn[1]) return 0;
	if(x1 <= a[k].mn[0] && y1 <= a[k].mn[1] && x2 >= a[k].mx[0] && y2 >= a[k].mx[1]) return a[k].si;
	int ans = (a[k].p[0] >= x1 && a[k].p[1] >= y1 && a[k].p[0] <= x2 && a[k].p[1] <= y2);
	ans += query(a[k].c[0]);
	ans += query(a[k].c[1]);
	return ans;
}
int solve(int k , int l , int r , int x)
{
	if(l == r) return l;
	int mid = (l + r) >> 1 , sum = query(root[rs[x]]);
	if(sum >= k) return solve(k , mid + 1 , r , rs[x]);
	else return solve(k - sum , l , mid , ls[x]);
}
int build(int l , int r , int now)
{
	if(l > r) return 0;
	int mid = (l + r) >> 1 , pos;
	d = now , nth_element(sta + l , sta + mid , sta + r + 1 , cmp) , pos = sta[mid];
	a[pos].c[0] = build(l , mid - 1 , now ^ 1);
	a[pos].c[1] = build(mid + 1 , r , now ^ 1);
	pushup(pos);
	return pos;
}
void dfs(int k)
{
	if(!k) return;
	sta[++top] = k;
	dfs(a[k].c[0]) , dfs(a[k].c[1]);
}
void rebuild(int &k)
{
	top = 0 , dfs(k);
	k = build(1 , top , 0);
}
void add(int p , int l , int r , int &x)
{
	if(!x) x = ++tot;
	d = 0 , insert(root[x]) , ts[x] ++ ;
	if(m == 50000 && ts[x] >= 2000) rebuild(root[x]) , ts[x] = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) add(p , l , mid , ls[x]);
	else add(p , mid + 1 , r , rs[x]);
}
int main()
{
	a[0].mx[0] = a[0].mx[1] = -M , a[0].mn[0] = a[0].mn[1] = M;
	int ans = 0 , opt , v , i;
	scanf("%*d%d" , &m);
	for(i = 1 ; i <= m ; i ++ )
	{
		scanf("%d" , &opt);
		if(opt == 1)
		{
			scanf("%d%d%d" , &x1 , &y1 , &v);
			x1 ^= ans , y1 ^= ans , v ^= ans;
			add(v , 1 , M , R);
		}
		else
		{
			scanf("%d%d%d%d%d" , &x1 , &y1 , &x2 , &y2 , &v);
			x1 ^= ans , y1 ^= ans , x2 ^= ans , y2 ^= ans , v ^= ans;
			if(query(root[R]) < v) puts("NAIVE!ORZzyz.") , ans = 0;
			else printf("%d\n" , ans = solve(v , 1 , M , R));
		}
	}
	return 0;
}

【bzoj4605】嶗山白花蛇草水 權值線段樹套KD-tree