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]);
}

 

posted @ 2019-04-13 10:01  uid001  阅读(148)  评论(0编辑  收藏  举报