您的位置:首页 > 其它

线段树单标记下穿【模板】

2018-03-19 12:33 288 查看
#include<iostream> //原谅我又用了好多英文.. 
#define mid (l+r)/2
using namespace std;
const int maxn=1000000;
long long sum[maxn],flag[maxn],size[maxn]; //sum为求和  flag为懒标记 size为区间长度 
int n,m,x,y,z,a[maxn]; //n为元素个数 m为询问个数  x,y为询问区间   z为更改的值  a[]储存元素的值 

void build(int t,int l,int r) //建树 
{
size[t]=r-l+1; //更新区间长度 
if(l==r) //叶子节点返回 
{
sum[t]=a[l];
return;
}
build(t*2,l,mid); //递归建树 
build(t*2+1,mid+1,r);
sum[t]=sum[t*2]+sum[t*2+1]; //回溯时更新sum 
}

void pushdown(int t) //懒标记下传 
{
sum[t]+=size[t]*flag[t]; //总和加上区间长度*修改值 
if(size[t]!=1) //懒标记下放到两个叶子节点 
{
flag[t*2]+=flag[t];
flag[t*2+1]+=flag[t];
}
flag[t]=0; //懒标记清零 
}

void add(int t,int l,int r) //区间加 
{
pushdown(t); //先更新真实值 
if(x<=l&&r<=y) //如果当前区间包含在修改区间中,更新懒标记 
{
flag[t]+=z;
return;
}
long long len=min(r,y)-max(l,x)+1; //如果没有在区间中,就找当前区间要修改的元素个数 
sum[t]+=len*z; //修改 
if(x<=mid)add(t*2,l,mid); //向下递归修改 
if(y>mid)add(t*2+1,mid+1,r);
}

long long query(int t,int l,int r) //查询 
{
pushdown(t); //懒标记下传,更新当前节点的真实值 
if(x<=l&&r<=y) return sum[t];
long long temp=0;
if(x<=mid)temp+=query(t*2,l,mid); //递归询问 
if(y>mid)temp+=query(t*2+1,mid+1,r);
return temp; //返回区间和 
}

int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i]; //读入数据 
build(1,1,n); //建树 
for(int i=1;i<=m;i++) //询问 
{
int que=0;
cin>>que;
if(que==1) //区间修改 
{
cin>>x>>y>>z;
add(1,1,n);
}
else //区间求和 
{
cin>>x>>y;
cout<<query(1,1,n)<<endl;
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: