树状数组模板——cdq分治入门

思路:

开始学cdq分治,然后看到了__stdcall的博客,然后发现从前打过的归并排序求逆序对就是一个典型的cdq分治。
大致思想就是先按照一维来排序,然后按照第一维的顺序进行分治。然后在分治的过程中保证在第一维下左半部分永远小于右半部分,然后就利用这个性质在左半边右半边分别按照第二维度排序,由于左边和右边都是处理好了的,所以只需要处理左边对于右边的贡献,前面既保证了第一位有序,又保证了第二维有序,所以时间的复杂度在每一层分治上面都是线性的。
理解了一下之后再看树状数组的模板发现每一个操作可以表示为一个二元组 (a,b) ( a , b ) a a 表示时间,b表示位置,我们所求的就是对于每一个询问操作,时间序在它之前并且位置在它之前的修改操作的和,基本上打法就和归并一样。

/*======================
 * Author : ylsoi
 * Problem : luogu3374
 * Algorithm : cdq
 * Time : 2018.7.29
 * ===================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("luogu3374.in","r",stdin);
    freopen("luogu3374.out","w",stdout);
}

const int maxn=5e5+10;
int n,m,a[maxn],sum[maxn],tot;
struct node{
    int id,pos,val;
    bool ty;
}b[maxn<<1],tmp[maxn<<1];
bool cmp(node xx,node yy){return xx.id!=yy.id ? xx.id<yy.id : xx.pos<yy.pos;}

#define mid ((l+r)>>1)
void cdq(int l,int r){
    if(l==r)return;
    cdq(l,mid); cdq(mid+1,r);
    int p=l,pl=l,pr=mid+1,s=0;
    while(pl<=mid && pr<=r){
        if(b[pl].pos<=b[pr].pos){
            if(!b[pl].ty)s+=b[pl].val;
            tmp[p++]=b[pl++];
        }
        else{
            if(b[pr].ty)b[pr].val+=s;
            tmp[p++]=b[pr++];
        }
    }
    while(pl<=mid)tmp[p++]=b[pl++];
    while(pr<=r){
        if(b[pr].ty)b[pr].val+=s;
        tmp[p++]=b[pr++];
    }
    REP(i,l,r)b[i]=tmp[i];
}

int main(){
    File();
    scanf("%d%d",&n,&m);
    REP(i,1,n){
        scanf("%d",&a[i]);
        sum[i]=sum[i-1]+a[i];
    }
    REP(i,1,m){
        int ty,x,y;
        scanf("%d%d%d",&ty,&x,&y);
        if(ty==1)b[++tot]=(node){i,x,y,0};
        else{
            b[++tot]=(node){i,x-1,0,1};
            b[++tot]=(node){i,y,0,1};
        }
    }
    cdq(1,tot);
    sort(b+1,b+tot+1,cmp);
    REP(i,1,tot){
        if(b[i].ty){
            printf("%d\n",b[i+1].val-b[i].val+sum[b[i+1].pos]-sum[b[i].pos]);
            ++i;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值