Time Limit: 5000MS | Memory Limit: 131072K | |
Total Submissions: 128968 | Accepted: 40007 | |
Case Time Limit: 2000MS |
Description
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.
Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.
Output
You need to answer all Q commands in order. One answer in a line.
Sample Input
10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4
Sample Output
4 55 9 15
Hint
Source
POJ Monthly--2007.11.25, Yang Yi
解析:
这道题可以用以下几种方法求解:
1.树状数组 O((N+Q)logN)
2.线段树 O((N+Q)logN)
3.分块 O((N+Q)√N ̄)
4.朴素 ((N+Q)*N)
在这里我着重说一下分块。
我们可以把数列A分成长度不超过[√N]的段。设add[i]为第i段的增量标记,sum[i]表示第i段的和。
对于操作 C l r d:
1.如果l和r同时处于第i段,则直接把a[l]......a[r]都加上d,同时sum[i]+=d*(r-l+1)。
2.如果l处于p段,r处于q段。那么对于i∈[p+1,q-1],令add[i]+=d。对于开头,结尾不足一整段的两部分,
按照第一种情况操作即可。
对于询问 Q l r:
1.如果l和r同时处于第i段,则ans=(a[l]+......+a[r])+add[i]*(r-l+1)。
2.如果l处于p段,r处于q段。则对于i∈[p+1,q-1],令ans+=sum[i]+add[i]*len[i],其中len[i]表示第i段的长度。
对于开头结尾不足一段的两部分,按照第一种方法累加即可。
总之,分块的基本思想可以被概括为:大段维护,局部朴素。
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
const int Max=110010;
int n,q,m,tot;
long long sum[Max],add[Max],num[Max];
int L[Max],R[Max];
int father[Max];
inline void Add(int l,int r,long long val)
{
int p=father[l],q=father[r];
if(p==q)
{
for(int i=l;i<=r;i++) num[i]+=val;
sum[p]+=(r-l+1)*val;
}
else
{
for(int i=p+1;i<=q-1;i++) add[i]+=val;
for(int i=l;i<=R[p];i++) num[i]+=val;
for(int i=L[q];i<=r;i++) num[i]+=val;
sum[p]+=(R[p]-l+1)*val;sum[q]+=(r-L[q]+1)*val;
}
}
inline long long ask(int l,int r)
{
int p=father[l],q=father[r];
long long ans=0;
if(p==q)
{
for(int i=l;i<=r;i++) ans+=num[i];
ans+=add[p]*(r-l+1);
}
else
{
for(int i=p+1;i<=q-1;i++) ans+=sum[i]+add[i]*(R[i]-L[i]+1);
for(int i=l;i<=R[p];i++) ans+=num[i];
for(int i=L[q];i<=r;i++) ans+=num[i];
ans+=(R[p]-l+1)*add[p]+(r-L[q]+1)*add[q];
}
return ans;
}
int main()
{
scanf("%d%d",&n,&q);
for(int i=1;i<=n;i++) scanf("%lld",&num[i]);
scanf("\n");
m=sqrt(n);
for(int i=1;i<=m;i++)
{
L[i]=(i-1)*m+1;
R[i]=i*m;
}
if(R[m]<n) {m++;L[m]=R[m-1]+1;R[m]=n;}
for(int i=1;i<=m;i++)
for(int j=L[i];j<=R[i];j++)
{
father[j]=i;
sum[i]+=num[j];
}
while(q--)
{
int x,y;
char c=getchar();
scanf("%d%d",&x,&y);
if(c=='Q') cout<<ask(x,y)<<"\n";
else
{
int z;
scanf("%d",&z);
Add(x,y,z);
}
scanf("\n");
}
return 0;
}