luogu【模板】三維偏序(陌上花開)
阿新 • • 發佈:2018-11-29
嘟嘟嘟
很顯然我開始學\(CDQ\)分治了。
我剛開始學的時候看了一篇部落格,上面全是一些抽象的概念,看完後真是一頭霧水,最後還不得不抄了這題的程式碼。
但這樣可不行呀……
於是我就不打算再扣那篇部落格,而是自己想,最後真的自己想明白了。
(個人感覺這道題跟\(CDQ\)分治關係不大)
首先想一下二維偏序:我們先按第一維排序,然後第二維動態的用樹狀陣列維護。也就是說,得保證前幾維都有序的前提下,才可以用資料結構維護最後一維。
現在換到了三維:所以我們必須保證在前兩維有序的前提下,才能維護第三維。
首先都能想到,按第一維排序,這樣就保證第一維有序了。
如何保證第二維\(y\)有序?如果硬排序,又會導致第一維\(x\)
答案是有的:歸併排序!對於區間\([L, R]\),雖然\([L, mid]\)和\([mid + 1, R]\)中的\(y\)有序,\(x\)亂序,但是\([mid + 1, R]\)中的任何一個元素的\(x\)仍是比\([L, mid]\)中的任何一個都大的!因此右區間相對於左區間保證了前兩維有序,所以此時我們就能用樹狀陣列維護第三維了!
這大概就是\(CDQ\)分治中的做修改只會對右詢問造成影響的意思吧。
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<cstdlib> #include<cctype> #include<vector> #include<stack> #include<queue> using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define rg register typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 1e5 + 5; const int maxm = 2e5 + 5; inline ll read() { ll ans = 0; char ch = getchar(), last = ' '; while(!isdigit(ch)) last = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(last == '-') ans = -ans; return ans; } inline void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } int n, m, k; struct Node { int x, y, z, cnt, sum; bool operator < (const Node& oth)const { if (x != oth.x) return x < oth.x; if (y != oth.y) return y < oth.y; return z < oth.z; } bool operator != (const Node& oth)const { return x != oth.x || y != oth.y || z != oth.z; } bool operator > (const Node& oth)const { if(y != oth.y) return y < oth.y; return z <= oth.z; } }a[maxn], t[maxn]; int c[maxm]; int lowbit(int x) { return x & -x; } void init(int pos) { for(; pos <= k; pos += lowbit(pos)) if(c[pos] != 0) c[pos] = 0; else break; } void add(int pos, int d) { for(; pos <= k; pos += lowbit(pos)) c[pos] += d; } int query(int pos) { int ret = 0; for(; pos; pos -= lowbit(pos)) ret += c[pos]; return ret; } void cdqSolve(int L, int R) { if(L == R) return; int mid = (L + R) >> 1, id1 = L, id2 = mid + 1; cdqSolve(L, mid); cdqSolve(mid + 1, R); for(int i = L; i <= R; ++i) { if(id2 > R || (id1 <= mid && a[id1] > a[id2])) //這個大於號是比較第二維的,不是真正的大於號 { t[i] = a[id1++]; add(t[i].z, t[i].cnt); } else { t[i] = a[id2++]; t[i].sum += query(t[i].z); } } for(int i = L; i <= R; ++i) a[i] = t[i], init(a[i].z);//分治的每一層,別忘了清空樹狀陣列 } int ans[maxn]; int main() { m = read(); k = read(); for(int i = 1; i <= m; ++i) t[i].x = read(), t[i].y = read(), t[i].z = read(); sort(t + 1, t + m + 1); for(int i = 2, j = 1; i <= m + 1; ++i) if(t[j] != t[i] || i == m + 1) a[++n] = t[j], a[n].cnt = i - j, j = i; cdqSolve(1, n); for(int i = 1; i <= n; ++i) ans[a[i].sum + a[i].cnt - 1] += a[i].cnt; for(int i = 0; i < m; ++i) write(ans[i]), enter; return 0; }