/*
线段树,注意传递参数的写法
*/
#include <cstdio>
#include <algorithm>
using namespace std;
#define LL long long
const int maxn = 1e5 + 10;
LL lazy[maxn<<2];
LL delta[maxn<<2];//代表一个根节点的历史改变量之和,解决一个区间被多次覆盖的问题
LL sum[maxn<<2];//保存答案
LL cal(LL x)
{
return x<0 ? -x:x;
}
void build(int l,int r,int rt)
{
if(l==r)
{
lazy[rt]=l;
return;
}
int mid=(l+r)>>1;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
lazy[rt]=0;
}
void clear(int v,int l,int r,int rt)//clear函数清空当前节点的标记,并且更新维护的节点的信息
{
if(lazy[rt]>0)
{
delta[rt]+=cal(v-lazy[rt]);
sum[rt]+=(r-l+1)*cal(v-lazy[rt]);
}
else
{
int mid=(l+r)>>1;
clear(v,l,mid,rt<<1);//清空子结点的值并且将子结点的值更新为正确值
clear(v,mid+1,r,rt<<1|1);
sum[rt]=sum[rt<<1]+sum[rt<<1|1]+delta[rt]*(r-l+1);//根据更新后的子结点的值更新父节点的值
}
}
void update(int L,int R,int v,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
clear(v,l,r,rt);//是为了在这次覆盖之前,重新结算该节点的信息,并维护子结点的信息
lazy[rt]=v;
return;
}
if(lazy[rt]>0)
{
lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
}
int mid=(l+r)>>1;
if(L<=mid) update(L,R,v,l,mid,rt<<1);
if(R>mid) update(L,R,v,mid+1,r,rt<<1|1);
lazy[rt]=0;
sum[rt]=sum[rt<<1]+sum[rt<<1|1]+delta[rt]*(r-l+1);
}
LL query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
{
return sum[rt];//只有当节点被覆盖时才直接返回
}
int mid = (l+r)>>1;
if(R<=mid) return query(L,R,l,mid,rt<<1)+delta[rt]*(R-L+1);//关键,此时对节点并不更新子结点的值,只是在不断下行的过程中,根据父节点的delta值更新答案
if(L>mid) return query(L,R,mid+1,r,rt<<1|1)+delta[rt]*(R-L+1);
return query(L,mid,l,mid,rt<<1)+query(mid+1,R,mid+1,r,rt<<1|1)+delta[rt]*(R-L+1);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
build(1,n,1);
while(m--)
{
int op;
scanf("%d",&op);
int l,r,x;
if(op==1)
{
scanf("%d%d%d",&l,&r,&x);
update(l,r,x,1,n,1);
}
else
{
scanf("%d%d",&l,&r);
printf("%lld\n",query(l,r,1,n,1));
}
}
return 0;
}
444 C DZY Loves Colors
最新推荐文章于 2021-11-01 16:09:16 发布