链接
http://codevs.cn/problem/1082/
题解
这道题目可以用分块做。
把原序列分成sqrt(N)块,对于每次修改,直接把两头的块暴力重构,中间的可以打上一个tag。查询时,暴力重构两头的(此时下放tag),中间的直接统计就好。
至此,这道题的三种做法就全了:
1.线段树
2.树状数组(升级版):http://blog.youkuaiyun.com/fsahfgsadhsakndas/article/details/52650026
3.分块:http://blog.youkuaiyun.com/fsahfgsadhsakndas/article/details/54772801
代码
//分块
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 300000
#define ll long long
using namespace std;
ll N, lp[maxn], tag[maxn], size, num[maxn], sum[maxn];
void input()
{
ll i;
scanf("%lld",&N);
size=sqrt(N);
for(i=0;i<N;i++)
{
scanf("%lld",num+i);
sum[lp[i]=i/size]+=num[i];
}
for(i=N;i<=250000;i++)lp[i]=i/size;
}
void rebuild(ll x)
{
ll i;
sum[x]=0;
for(i=x*size;lp[i]==x;i++)num[i]+=tag[x],sum[x]+=num[i];
tag[x]=0;
}
void add(ll l, ll r, ll v)
{
ll i;
for(i=l;lp[i]==lp[l] and i<=r;i++)num[i]+=v;
if(lp[l]!=lp[r])for(i=r;lp[i]==lp[r];i--)num[i]+=v;
rebuild(lp[l]), rebuild(lp[r]);
for(i=lp[l]*size+size;lp[i]<lp[r];i+=size)tag[lp[i]]+=v;
}
ll getsum(ll l, ll r)
{
ll i, ans=0;
rebuild(lp[l]), rebuild(lp[r]);
for(i=l;lp[i]==lp[l] and i<=r;i++)ans+=num[i];
if(lp[l]!=lp[r])for(i=r;lp[i]==lp[r];i--)ans+=num[i];
for(i=lp[l]*size+size;lp[i]<lp[r];i+=size)ans+=sum[lp[i]]+size*tag[lp[i]];
return ans;
}
int main()
{
ll a, b, v, M, type;
input();
scanf("%lld",&M);
while(M--)
{
scanf("%lld%lld%lld",&type,&a,&b);
if(type==1)scanf("%lld",&v),add(a-1,b-1,v);
else printf("%lld\n",getsum(a-1,b-1));
}
return 0;
}