题目大意:
题目链接:http://poj.org/problem?id=3468
给出一个初始数列,有两种操作:
- ,输出这个数列到之间的数字和。
- ,将这个数列的到之间的数加上。
思路:
这道题还可以用分块做。。。
模板的分块,就不多解释了。
把这个数列分成块,每块长度不超过,每块有如下几个变量:
- 表示第块里面所有数的总和
- 表示将第块整体加或减时的值
- 表示第块的最左边的位置
- 表示第块的最右边的位置
每一块都有一些关于数字的变量,那么每一个数字也有它的那一块的变量:
- 不用解释了。。。读入的初始值
- 数字属于第几块
过程详见代码:
代码:
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <iostream>
#define N 100011
#define ll long long
using namespace std;
int n,m,L[N],R[N],pos[N],x,y,t;
ll add[N],sum[N],a[N],z;
char c;
ll ask(int l,int r) //查找
{
int q=pos[l],p=pos[r]; //左右边界分别的块
ll ans=0;
if (q==p) //在同一块就直接暴力求答案(反正不会超过sqrt(n))
{
for (int i=l;i<=r;i++)
ans+=a[i]+add[q]; //a[i]是原来的答案,add[q]是整体加的时候这一块加的和。
return ans;
}
//不在同一块,就将中间的包含了整块整块的直接求出来
//然后在将左右两边用上述暴力方法求出
for (int i=q+1;i<p;i++)
ans+=add[i]*(ll)(R[i]-L[i]+1)+sum[i]; //中间整块的答案
for (int i=l;i<=R[q];i++)
ans+=a[i]+add[q]; //暴力左边
for (int i=L[p];i<=r;i++)
ans+=a[i]+add[p]; //暴力右边
return ans;
}
void Add(int l,int r,ll z) //修改
{
int q=pos[l],p=pos[r];
if (q==p) //在同一块就直接暴力
{
for (int i=l;i<=r;i++)
{
a[i]+=z; //直接暴力加上
sum[q]+=z; //这一块的总和也要加
}
return;
}
//还是像查找那样,先求出中间的整段的答案,两边在分开处理
for (int i=q+1;i<p;i++)
add[i]+=z; //中间整段都加上z
for (int i=l;i<=R[q];i++) //左边
{
a[i]+=z;
sum[q]+=z;
}
for (int i=L[p];i<=r;i++) //右边
{
a[i]+=z;
sum[p]+=z;
}
return;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%lld",&a[i]);
t=(int)sqrt(n); //总块数
for (int i=1;i<=t;i++)
{
L[i]=(i-1)*t+1;
R[i]=i*t; //求出每一快的长度和左右边界
for (int j=L[i];j<=R[i];j++)
{
pos[j]=i;
sum[i]+=a[j]; //初始化
}
}
if (R[t]<n) //有可能还会差一块,补上
{
L[++t]=R[t-1]+1;
R[t]=n;
for (int i=L[t];i<=R[t];i++)
{
pos[i]=t;
sum[t]+=a[i];
}
}
while (m--)
{
cin>>c;
scanf("%d%d",&x,&y);
if (c=='Q')
{
cout<<ask(x,y)<<endl;
}
else
{
scanf("%lld",&z);
Add(x,y,z);
}
}
return 0;
}