题解 P3372 【【模板】线段树 1】
zkw(重口味)线段树,详见zkw的PPT
zkw线段树的操作是从下到上的,没有回溯,用循环代替。各种操作的思想同普通线段树。
1.开long long
2.lb-getlong(k) 子树中叶节点个数
3.左子树a[i<<1],右子树a[i<<1|1]
4.a[]该节点子树的和,mark[]该节点被覆盖次数
5.zkw线段树只能查询(0,bit),所以只有bit-2个叶节点,bit-2<n要扩大
附上代码
#include <iostream>
#include<cstdio>
#define ll long long
using namespace std;
int n,m;
ll a[400005],mark[400005];
ll bit,lb;
inline int getlong(int k)//查询结点第几层
{
int i=0;
while(k>>=1)i++;
return i;
}
inline int put(int k,int t)//修改
{
int tmp=1<<(lb-getlong(k));
while(k>>=1)
a[k]+=t*tmp;
return 0;
}
inline ll get1(ll k)//查询
{
ll tmp=1<<(lb-getlong(k));tmp有多少叶节点就加几倍
ll re=a[k]+mark[k]*tmp;
do
{
k>>=1;
re+=mark[k]*tmp;
}while(k);
return re;
}
inline ll get(ll s,ll t)//区间查询
{
ll re=0;
for(s=s+bit-1,t=t+bit+1;s^t^1;s>>=1,t>>=1)//s-1和t+1变开区间,s+bit和t+bit到对应叶节点
{
if(!(s&1))
re+=get1(s^1);//*(1<<(lb-getlong(s)));
if(t&1)
re+=get1(t^1);//*(1<<(lb-getlong(t)));
}
return re;
}
inline int change(ll s,ll t,ll k)//区间修改
{
for(s=s+bit-1,t=t+bit+1;s^t^1;s>>=1,t>>=1)
{
//int tmp;
if(!(s&1))
{
mark[s^1]+=k;//*(1<<(lb-getlong(s)));
put(s^1,k);
}
if(t&1)
{
mark[t^1]+=k;//*(1<<(lb-getlong(t)));
put(t^1,k);
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n>>m;
for(bit=1;bit-2<n;bit<<=1);
lb=getlong(bit);
for(int i=bit+1;i<=bit+n;i++)
cin>>a[i];
for(int i=bit-1;i;i--)
{
a[i]=a[i<<1]+a[i<<1|1];//( i*2==i<<1) , (i*2+1==i<<1|1)
//a[i<<1]-=a[i];a[i<<1|1]-=a[i];
}
for(int i=1;i<=m;i++)
{
ll aa,b,c,d;
cin>>aa;
if(aa==2)
{
cin>>b>>c;//cout<<b<<' '<<c<<' '<<get(b,c)<<endl;
cout<<get(b,c)<<endl;
for(int i=1;i<=n+bit;i++)cout<<get1(i)<<' ';cout<<endl;
}
else
{
cin>>b>>c>>d;change(b,c,d);
//cout<<b<<' '<<c<<' '<<d<<endl;
for(int i=1;i<=n+bit;i++)cout<<get1(i)<<' ';cout<<endl;
}
}
//cout << "Hello world!" << endl;
return 0;
}