• CDQ分治


    例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]);
    }
    
  • 相关阅读:
    java集合归纳
    判断回文数
    29:四则运算计算表达式的值
    getOutString 输出弹出字符串
    两个字符串中不同元素的个数
    字符串各个字符ASCII值加5
    23:一个整数的二进制表示中有多少个1
    Java进程间通信
    转 双重检查锁定与延迟初始化
    Key-Value键值存储原理初识(NOSQL)
  • 原文地址:https://www.cnblogs.com/uid001/p/10699963.html
Copyright © 2020-2023  润新知