【洛谷P3810】 【模板】三维偏序(陌上花开)
CDQ是一中思想,用来求点对数列。
定义\(solve(l,r)\)用来求\([l,r]\)区间的数对,
那么先递归处理\(solve(l,mid)\),
然后考虑前半段对后半段的影响,
然后再递归处理后半段\(solve(mid+1,r)\)
对于这道题,具体就是,先按\(a\)排序,然后\(solve(l,r)\)先递归处理两边,然后考虑怎么统计\(i\)在前半段,\(j\)在后半段的满足\(b_i\leq b_j\)且\(c_i\leq c_j\)的个数。
可以先对这两端按\(b\)排序,然后双指针一个个把左边的\(c\)插到树状数组里,枚举右边的直接统计。
#include <bits/stdc++.h>
#define lowbit(x) (x&(-x))
#define pb push_back
#define rep(i, m, n) for(int i = (m); i <= (n); ++i)
#define dop(i, m, n) for(int i = (m); i >= (n); --i)
#define all(x) x.begin(), x.end()
#define Open(s) freopen(s,"r",stdin);
#define Write(s) freopen(s,"w",stdout);
using namespace std;
typedef long long ll;
template <class T> void chkmin(T &a, T b){ if(a > b) a = b; }
template <class T> void chkmax(T &a, T b){ if(a < b) a = b; }
template <class T> T aabs(T a){ return a > 0 ? a : -a; }
const int N = 200010;
struct node{
int num, a, b, c, ans;
void input(){
scanf("%d%d%d", &a, &b, &c);
}
}q[N], p[N];
int n, k;
int cmp(const node& a, const node& b){
return a.b != b.b ? a.b < b.b : a.c < b.c;
};
int s[N], cnt[N];
void add(int x, int y){
while(x <= k){
s[x] += y;
x += lowbit(x);
}
}
int ask(int x){
int ans = 0;
while(x){
ans += s[x];
x -= lowbit(x);
}
return ans;
}
void solve(int l, int r){
if(l == r) return;
int mid = l + r >> 1;
solve(l, mid); solve(mid + 1, r);
sort(p+l, p+mid+1, cmp);
sort(p+mid+1, p+r+1, cmp);
int cur = l - 1;
rep(i, mid+1, r){
while(cur < mid && p[cur+1].b <= p[i].b){
++cur; add(p[cur].c, p[cur].num);
}
p[i].ans += ask(p[i].c);
}
rep(i, l, cur) add(p[i].c, -p[i].num);
}
int main(){
scanf("%d%d", &n, &k); int m = n;
rep(i, 1, n) q[i].input();
sort(q+1, q+n+1, [](node& a, node& b){
return a.a != b.a ? a.a < b.a : (a.b != b.b ? a.b < b.b : a.c < b.c);
});
n = 0;
rep(i, 1, m){
if(q[i].a != q[i-1].a || q[i].b != q[i-1].b || q[i].c != q[i-1].c){
p[++n] = q[i]; p[n].num = 1;
}
else p[n].num++;
}
solve(1, n);
rep(i, 1, n) cnt[p[i].ans + p[i].num - 1] += p[i].num;
rep(i, 0, m-1) printf("%d\n", cnt[i]);
return 0;
}