题意:
就是有个初始序列,然后m次操作,一种是让l到r都加上k,一种是查询l到r的区间和。
思考:
其实就是线段树板子题,这里用来练习分块。
代码:
int T,n,m,k;
int va[N];
int pos[N],l[N],r[N],sum[N],add[N],cnt;
/*va是某个点的值,sum是某个块的值,add是某个块的标记
为什么还要add呢,因为如果更新块的时候,直接更新了sum。如果下次查询l,r你说怎么累加这个和,如果输出sum[pos[l]]这太大了,如果输出l到r的va值,这又是错的因为你只更新了sum没有更新va,所以为了这样的情况,要维护3个,一个是单点值,一个是某个块的值,一个是标记。
*/
void build()
{
int siz = sqrt(n);
cnt = (n+siz-1)/siz;
for(int i=1;i<=cnt;i++)
{
l[i] = (i-1)*siz+1;
r[i] = i*siz;
}
r[cnt] = n;
for(int i=1;i<=n;i++) pos[i] = (i-1)/siz+1;
for(int i=1;i<=n;i++) sum[pos[i]] += va[i];
}
void update(int a,int b,int value)
{
if(pos[a]==pos[b])
{
for(int i=a;i<=b;i++) va[i] += value;
sum[pos[a]] += (b-a+1)*value;
return ;
}
for(int i=a;i<=r[pos[a]];i++) va[i] += value;
sum[pos[a]] += (r[pos[a]]-a+1)*value;
for(int i=l[pos[b]];i<=b;i++) va[i] += value;
sum[pos[b]] += (b-l[pos[b]]+1)*value;
for(int i=pos[a]+1;i<pos[b];i++) add[i] += value;
}
int query(int a,int b)
{
int ans = 0;
if(pos[a]==pos[b])
{
for(int i=a;i<=b;i++) ans += va[i];
ans += add[pos[a]]*(b-a+1);
return ans;
}
for(int i=a;i<=r[pos[a]];i++) ans += va[i];
ans += add[pos[a]]*(r[pos[a]]-a+1);
for(int i=l[pos[b]];i<=b;i++) ans += va[i];
ans += add[pos[b]]*(b-l[pos[b]]+1);
for(int i=pos[a]+1;i<pos[b];i++) ans += sum[i]+add[i]*(r[i]-l[i]+1);
return ans;
}
signed main()
{
IOS;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>va[i];
build();
while(m--)
{
int op,a,b,c;
cin>>op>>a>>b;
if(op==1)
{
cin>>c;
update(a,b,c);
}
else cout<<query(a,b)<<"\n";
}
return 0;
}
总结:
多多积累经验,多思考思考。