例1. 给定序列, 2种操作, (1)单点加 (2)区间求和
简单的树状数组操作, 我们考虑用CDQ分治的做法. 询问看成二元组$(t,x)$, $t$为操作时间, $x$为操作位置, 区间求和转化为两个前缀求和做差, 那么问题就等价于求所有$ttle t$, $xxle 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 ", ans[i]); }
例2. 陌上花开
大意: 给定n个三元组(a,b,c), 设$f(i)$为$a_jle a_i,b_jle b_i,c_jle c_i$的$j$的数量, 对于$din[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 ", ans[i]); }
例3. Mokia
大意: 给定w*w矩阵, 两种操作, 单点增加, 询问子矩阵和.
考虑三元组(t,x,y), t为操作时间, x,y为坐标, 子矩阵和拆成4个前缀和, 等价于求所有满足$ttle t,xxle x,yyle 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 ", ans[i]); }