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