• CDQ分治学习笔记


    CDQ分治学习笔记

    1.算法思想

    分而治之;然后统计 [ l , m i d ] [l,mid] [l,mid] [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的贡献。

    主要难点:统计左半部分对右半部分的贡献;

    主要套路:利用左半部分有序的特点降低常数;把修改等操作转化为偏序关系。

    2.使用场景

    多维偏序或者一些很难直接处理但是可以转化为偏序关系的题目。

    有修改的也可以转化为多了时间维的偏序关系。

    3.例题

    洛谷 P3810 【模板】三维偏序(陌上花开)

    先去重!每个点就是一个非重的三维点对(记得记录出现次数)。

    然后预处理先按照第一关键字排序,然后 cdq 分治。

    每层在回溯的时候排序第二关键字(可以归并排序)。然后双指针的同时用树状数组记录第三关键字出现的次数。

    细节有点多。

    Generator(gmoj.net)

    题意:【模板】线段树1,但是一开始没有操作,陆陆续续在某些位置上面加上操作,求每次加上操作的答案。可离线。

    思路:(比赛时没打暴力,差评,能骗到很多很多部分分的)

    考虑最朴素的做法:按照时间一个个加入操作。

    如果这是询问操作的话那么答案就要加上只时间在其前面且位置在其前面的所有修改操作对它的贡献区间和统计即可。

    如果这是修改操作的话那么答案也要加上它对时间在其后面且位置在其后面的所有查询操作的贡献。这个也需区间和统计。

    然后理清思路,那么就变成了操作 i i i,要么 KaTeX parse error: Undefined control sequence: \and at position 42: …]\{j\in{change}\̲a̲n̲d̲ ̲i\in query\} 或者 KaTeX parse error: Undefined control sequence: \and at position 40: …[j]\{j\in query\̲a̲n̲d̲ ̲i\in change\}

    然后考虑按照 t i m tim tim 顺序直接 CDQ 分治,就解决掉了 t i m tim tim 这维的偏序问题。

    那么我们思考 [ l , m i d ] [l,mid] [l,mid] [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 的答案关系?只需统计左边部分对右边部分的贡献即可。

    然后使用归并排序,回溯时候合并两个序列,按照 o r d ord ord 排序(这也是 CDQ 分治的一部分),这就解决了 o r d ord ord 的顺序问题。

    合并的时候不一定要用双指针扫描法,按照 o r d ord ord 排序之后判断 t i m ≤ m i d tim\le mid timmid 即可判断原来在左还是在右,而且能够保证 o r d ord ord 顺序。

    #include
    #define getchar() (p1==p2&&(p1=buf,p2=p1+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
    char buf[1<<20],*p1=buf,*p2=buf;
    using namespace std;typedef int I;typedef long long LL;const I inf=1073741823;I CH,FL;template<typename T>bool in(T&a){for(FL=1;CH!=EOF&&!isdigit(CH);CH=getchar())if(CH=='-')FL=-1;for(a=0;CH!=EOF&&isdigit(CH);CH=getchar())a=a*10+CH-'0';return a*=FL,CH!=EOF;}template<typename T,typename...Args>bool in(T&a,Args&...args){return in(a)+in(args...);}
    const I maxn=2e5+50;
    struct qry{
    	I l,r,val,ord,tim;bool tp;
    }Q[maxn],B[maxn];
    bool cmp(qry a,qry b){return a.tim<b.tim;}
    I blen,opnum[maxn],n,q;bool CR[maxn<<1];LL ans[maxn];
    struct BIT{
    	LL S[maxn];
    	void pls(I x,LL y){for(;x<=n+1;x+=x&-x)S[x]+=y;}
    	LL qry(I x){LL ans=0;for(;x;x-=x&-x)ans+=S[x];return ans;}
    }C,CI;
    void ppls(I l,I r,I val){C.pls(l,val);C.pls(r+1,-val);CI.pls(l,1ll*val*l);CI.pls(r+1,-1ll*(r+1)*val);}
    LL qsum(I x){LL res0=C.qry(x),res1=CI.qry(x);return (x+1)*res0-res1;}
    LL qqry(I l,I r){return qsum(r)-qsum(l-1);}
    void cdq(I l,I r){
    	if(l==r)return;
    	I mid=l+r>>1,i,j,l1=l-1,r1=r+1,ti,tp;;cdq(l,mid);cdq(mid+1,r);
    	for(blen=0,i=mid+1,j=l;i^r1;++i){
    		while(j<=mid&&Q[j].ord<Q[i].ord)B[++blen]=Q[j],++j;
    		B[++blen]=Q[i];
    	}while(j<=mid)B[++blen]=Q[j],++j;
    	for(i=l;i^r1;++i)Q[i]=B[i-l+1];
    	for(i=l,ti=Q[i].tim,tp=Q[i].tp;i^r1;++i,ti=Q[i].tim,tp=Q[i].tp){
    		if(ti<=mid&&tp==0)ppls(Q[i].l,Q[i].r,Q[i].val);
    		if(ti>mid&&tp==1)ans[ti]+=qqry(Q[i].l,Q[i].r);
    	}for(i=r,ti=Q[i].tim,tp=Q[i].tp;i^l1;--i,ti=Q[i].tim,tp=Q[i].tp)
    		if(ti<=mid&&tp==0)ppls(Q[i].l,Q[i].r,-Q[i].val);
    	for(i=r,ti=Q[i].tim,tp=Q[i].tp;i^l1;--i,ti=Q[i].tim,tp=Q[i].tp){
    		if(ti<=mid&&tp==1)ppls(Q[i].l,Q[i].r,1);
    		if(ti>mid&&tp==0)ans[ti]+=qqry(Q[i].l,Q[i].r)*Q[i].val;
    	}for(i=r,ti=Q[i].tim,tp=Q[i].tp;i^l1;--i,ti=Q[i].tim,tp=Q[i].tp)
    		if(ti<=mid&&tp==1)ppls(Q[i].l,Q[i].r,-1);
    }I main(){
    	freopen("generator.in","r",stdin);
    	freopen("generator.out","w",stdout);
    	in(n,q);
    	for(I i=1;i<q;++i)in(opnum[i]),Q[opnum[i]].tim=i;
    	for(I i=1,op;i<=q;++i){
    		in(op,Q[i].l,Q[i].r);Q[i].tp=op-1;Q[i].ord=i;
    		if(op==1)in(Q[i].val);
    		++Q[i].tim;
    	}sort(Q+1,Q+q+1,cmp);
    	cdq(1,q);
    	for(I i=1;i<=q;++i){
    		ans[i]+=ans[i-1];
    		printf("%lld\n",ans[i]);
    	}return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  • 相关阅读:
    请别再使用 parseInt() 了
    零基础程序员想要学好.Net,跟着这7个步骤学习就可以了
    HTTP协议
    算法day22|235,701,450
    kafka常用命令
    图形库篇 | EasyX | 基本介绍
    服务器挂机
    matlab实现了一个基于粒子群优化(PSO)算法的程序,用于寻找一种三层材料结构的最佳配置
    springboot整合tomcat源码浅析
    如何验证高压放大器的性能好坏呢
  • 原文地址:https://blog.csdn.net/STJqwq/article/details/126336493