CDQ分治学习

例1. 给定序列, 2种操作, (1)单点加 (2)区间求和

询问看成二维键值对$(t,x)$, $t$为操作时间, $x$为操作位置, 区间求和转化为两个前缀求和做差, 那么问题就等价于求所有$tt\le t$, $xx\le x$的权值和, 也就是一个二维偏序问题, 时间已经默认有序, 直接对操作位置归并排序一次即可求出. 要注意操作位置相同时, 询问操作要排在前面.

#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个三元组, 对于每个三元组(x,y,z), 求有多少三元组$(x_0,y_0,z_0)$, 满足$x<x_0,y<y_0,z<z_0$

猜你喜欢

转载自www.cnblogs.com/uid001/p/10699963.html