CDQ分治
例1. 给定序列, 2种操作, (1)单点加 (2)区间求和
简单的树状数组操作, 我们考虑用CDQ分治的做法. 询问看成二元组$(t,x)$, $t$为操作时间, $x$为操作位置, 区间求和转化为两个前缀求和做差, 那么问题就等价于求所有$tt\le t$, $xx\le x$的二元组(tt,xx)的权值和, 也就是一个二维偏序问题, 时间已经默认有序, 直接对操作位置归并排序一次即可求出. 要注意操作位置相同时, 询问操作要排在前面.
#include <iostream>
#include <algorithm>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
const int N = 3e6+10;
int n, m, tot, totx;
struct _ {
int type,id,v;
bool operator < (const _ &rhs) const {
return id<rhs.id||id==rhs.id&&type<rhs.type;
}
} q[N], tmp[N];
int ans[N];
void merge(int l, int r) {
if (l==r) return;
int mid = l+r>>1;
merge(l,mid),merge(mid+1,r);
int p1=l, p2=mid+1, s=0;
REP(i,l,r) {
if (p2>r||(p1<=mid&&q[p1]<q[p2])) {
if (q[p1].type==0) s+=q[p1].v;
tmp[i]=q[p1++];
}
else {
if (q[p2].type==2) ans[q[p2].v]+=s;
else if (q[p2].type==1) ans[q[p2].v]-=s;
tmp[i]=q[p2++];
}
}
REP(i,l,r) q[i]=tmp[i];
}
int main() {
scanf("%d%d", &n, &m);
REP(i,1,n) {
++tot;
scanf("%d", &q[tot].v);
q[tot].id = i;
}
REP(i,1,m) {
int op, l, r, x, v;
scanf("%d", &op);
if (op==1) {
scanf("%d%d", &x, &v);
q[++tot]={0,x,v};
}
else {
++totx,scanf("%d%d", &l, &r);
q[++tot]={1,l-1,totx};
q[++tot]={2,r,totx};
}
}
merge(1,tot);
REP(i,1,totx) printf("%d\n", ans[i]);
}
例2. 陌上花开
大意: 给定n个三元组(a,b,c), 设$f(i)$为$a_j\le a_i,b_j\le b_i,c_j\le c_i$的$j$的数量, 对于$d\in[0,n)$, 求$f(i)=d$的数量.
三维偏序板子题, 一维排序, 二维归并, 三维树状数组. 需要特判重复元素
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
const int N = 3e6+10;
int n, m, tot, ans[N];
struct _ {
int a,b,c,ans,cnt;
bool operator == (const _ &rhs) const {
return a==rhs.a&&b==rhs.b&&c==rhs.c;
}
} a[N], b[N];
bool cmp1(const _ &lhs, const _ &rhs) {
if (lhs.a!=rhs.a) return lhs.a<rhs.a;
if (lhs.b!=rhs.b) return lhs.b<rhs.b;
return lhs.c<rhs.c;
}
bool cmp2(const _ &lhs, const _ &rhs) {
if (lhs.b!=rhs.b) return lhs.b<rhs.b;
return lhs.c<rhs.c;
}
int clk, c[N], tim[N], k;
void add(int x, int v) {
for (; x<=k; x+=x&-x) tim[x]==clk?c[x]+=v:c[x]=v,tim[x]=clk;
}
int query(int x) {
int r = 0;
for (; x; x^=x&-x) r+=tim[x]==clk?c[x]:0;
return r;
}
void merge(int l, int r) {
if (l==r) return b[l].ans+=b[l].cnt-1,void();
int mid = l+r>>1;
merge(l,mid),merge(mid+1,r);
int now = l;
++clk;
REP(i,mid+1,r) {
while (now<=mid&&b[now].b<=b[i].b) {
add(b[now].c, b[now].cnt);
++now;
}
b[i].ans += query(b[i].c);
}
inplace_merge(b+l,b+mid+1,b+r+1);
}
int main() {
scanf("%d%d", &n, &k);
REP(i,1,n) scanf("%d%d%d", &a[i].a, &a[i].b, &a[i].c);
sort(a+1,a+1+n,cmp1);
REP(i,1,n) {
if (i>1&&a[i]==a[i-1]) ++b[tot].cnt;
else b[++tot]=a[i],b[tot].cnt=1;
}
merge(1,tot);
REP(i,1,tot) ans[b[i].ans]+=b[i].cnt;
REP(i,0,n-1) printf("%d\n", ans[i]);
}
例3. Mokia
大意: 给定w*w矩阵, 两种操作, 单点增加, 询问子矩阵和.
考虑三元组(t,x,y), t为操作时间, x,y为坐标, 子矩阵和拆成4个前缀和, 等价于求所有满足$tt\le t,xx\le x,yy\le y$的三元组(tt,xx,yy)的权值.
#include <iostream>
#include <algorithm>
#include <cstdio>
#define REP(i,a,n) for(int i=a;i<=n;++i)
#define PER(i,a,n) for(int i=n;i>=a;--i)
using namespace std;
typedef long long ll;
const int N = 1e6+10;
int w, tot, totx;
struct _ {
int type,x,y,v;
bool operator < (const _ & rhs) const {
if (x!=rhs.x) return x<rhs.x;
if (y!=rhs.y) return y<rhs.y;
return type<rhs.type;
}
} e[N];
ll ans[N], c[N<<1];
void add(int x, int v) {
for (++x; x<=w+1; x+=x&-x) c[x]+=v;
}
ll qry(int x) {
ll r = 0;
for (++x; x; x^=x&-x) r+=c[x];
return r;
}
void merge(int l, int r) {
if (l==r) return;
int mid = l+r>>1;
merge(l,mid),merge(mid+1,r);
int now = l;
REP(i,mid+1,r) {
while (now<=mid&&e[now].x<=e[i].x) {
if (e[now].type==0) add(e[now].y,e[now].v);
++now;
}
if (e[i].type==1) ans[e[i].v]+=qry(e[i].y);
else if (e[i].type==2) ans[e[i].v]-=qry(e[i].y);
}
while (now!=l) {
if (e[--now].type==0) add(e[now].y,-e[now].v);
}
inplace_merge(e+l,e+mid+1,e+r+1);
}
int main() {
scanf("%*d%d", &w);
for (int op; scanf("%d",&op),op!=3; ) {
int x, y, x2, y2, v;
if (op==1) {
scanf("%d%d%d", &x, &y, &v);
e[++tot] = {0,x,y,v};
}
else {
scanf("%d%d%d%d", &x, &y, &x2, &y2),++totx;
e[++tot] = {1,x2,y2,totx};
e[++tot] = {1,x-1,y-1,totx};
e[++tot] = {2,x-1,y2,totx};
e[++tot] = {2,x2,y-1,totx};
}
}
merge(1,tot);
REP(i,1,totx) printf("%lld\n", ans[i]);
}

浙公网安备 33010602011771号