【bzoj4605】嶗山白花蛇草水 權值線段樹套KD-tree
題目描述
神犇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不屑於做這種問題,所以把這個問題交給了你。輸入
輸出
樣例輸入
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