链接:点击打开链接
题意:给出n个数,初始为1~n,有m个操作,一种是将区间[l,r]内的值改为x,同时改点会累加一个|x-y|的值,第二种询问[l,r]区间内产生的累加值的和
代码:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
using namespace std;
const long long SIZE=100005; //用两个标记,一个是懒惰标记,另一个是判断该节点表示的
long long cnt[SIZE<<2],add[SIZE<<2],sum[SIZE<<2];
void pushup(int rt){ //区间是否是同一个值
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
if(cnt[rt<<1]&&cnt[rt<<1]==cnt[rt<<1|1])
cnt[rt]=cnt[rt<<1];
else
cnt[rt]=0;
}
void pushdown(long long rt,long long m){
if(add[rt]){
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
sum[rt<<1]+=add[rt]*(m-(m>>1));
sum[rt<<1|1]+=add[rt]*(m>>1);
cnt[rt<<1]=cnt[rt<<1|1]=cnt[rt];
add[rt]=0;
}
}
void build(long long l,long long r,long long rt){
long long m;
add[rt]=sum[rt]=0;
if(l==r){
cnt[rt]=l;
return ;
}
m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);
}
void update(long long L,long long R,long long c,long long l,long long r,long long rt){
long long m;
if(L<=l&&r<=R&&cnt[rt]){ //同一个值才更新
add[rt]+=abs(cnt[rt]-c);
sum[rt]+=abs(cnt[rt]-c)*(r-l+1); //不是同一个值就无法直接懒惰标记更新
cnt[rt]=c;
return;
}
pushdown(rt,r-l+1);
m=(l+r)>>1;
if(L<=m)
update(L,R,c,l,m,rt<<1);
if(R>m)
update(L,R,c,m+1,r,rt<<1|1);
pushup(rt);
}
long long query(long long L,long long R,long long l,long long r,long long rt){
long long m,ans;
if(L<=l&&r<=R)
return sum[rt];
pushdown(rt,r-l+1);
ans=0;
m=(l+r)>>1;
if(L<=m)
ans+=query(L,R,l,m,rt<<1);
if(R>m)
ans+=query(L,R,m+1,r,rt<<1|1);
return ans;
} //线段树区间询问
int main(){
long long n,m,i,j,a,b,c,ch,sign;
while(scanf("%I64d%I64d",&n,&m)!=EOF){
build(1,n,1);
while(m--){
scanf("%I64d",&ch);
if(ch==2){
scanf("%I64d%I64d",&a,&b);
printf("%I64d\n",query(a,b,1,n,1));
}
else{
scanf("%I64d%I64d%I64d",&a,&b,&c);
update(a,b,c,1,n,1);
}
}
}
return 0;
}