~by Wjvje 2019-4-30
题目链接:http://poj.org/problem?id=3468
题目描述:
A Simple Problem with Integers
Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 155314 Accepted: 48062 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
The sums may exceed the range of 32-bit integers.
题意:给出一段区间,两种操作,区间修改或者区间求和,数据范围如上。
思路:区间求和+区间修改,线段树/树状数组均可:
线段树的加个lazy标记;
树状数组:因为有区间修改,所以树状数组的话加上差分;
又因为树状数组维护的是单个元素的增量数组,所以对于树状
数组做带有区间修改的区间查询要根据数学推导得出最终的求
解公式,百度一下有关内容挺详细的,然后最后根据推导出来
的式子维护两个树状数组即可。
线段树代码:(区间修改lazy标记)
// 求和、数组啥的改成LL ,懒得改了
/**Wjvje**/
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int N=4*2e5+100;
const int M=4e5+100;
using namespace std;
int a[N];
int dat[N],Left[N],Right[N];
int flag[N];
void build(int p,int l,int r)//只有建树时递归区间才会变;
{
Left[p]=l;
Right[p]=r;
if(l==r)
{
dat[p]=a[l];
return ;
}
int mid=(l+r)>>1;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
dat[p]=dat[2*p]+dat[2*p+1];
}
void spread(int p)
{
if(!flag[p])return ;
dat[2*p]+=flag[p]*(Right[2*p]-Left[p*2]+1);
dat[2*p+1]+=flag[p]*(Right[2*p+1]-Left[p*2+1]+1);
flag[2*p]+=flag[p];
flag[2*p+1]+=flag[p];
flag[p]=0;
}
void change(int p,int l,int r,int v)//递归时,查询区间是L,R时不变的;
{
if(l<=Left[p]&&r>=Right[p])
{
dat[p]+=v*(Right[p]-Left[p]+1);
flag[p]+=v;
return ;
}
int mid=(Left[p]+Right[p])>>1;
spread(p);
if(l<=mid)change(p*2,l,r,v);
if(r>mid)change(2*p+1,l,r,v);
dat[p]=dat[p*2]+dat[2*p+1];
}
int ask(int p,int l,int r)//递归时,查询区间是L,R时不变的;
{
if(l<=Left[p]&&r>=Right[p])return dat[p];
int mid=(Left[p]+Right[p])>>1;
LL val=0;
spread(p);
if(l<=mid)val+=ask(p*2,l,r);
if(r>mid)val+=ask(2*p+1,l,r);
return val;
}
int main()
{
io;
int n,m;
while(cin>>n>>m)
{
memset(dat,0,sizeof(dat));
for(int i=1;i<=n;++i)cin>>a[i];
build(1,1,n);
while(m--)
{
char ch;
int x,y;
int z;
cin>>ch>>x>>y;
if(ch=='Q')cout<<ask(1,x,y)<<endl;
else cin>>z,change(1,x,y,z);
}
}
return 0;
}
树状数组(区间修改(差分)+区间求和(数学化简))代码:
/**Wjvje**/
#include<bits/stdc++.h>
#define LL long long
#define INF 0x3f3f3f3f
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int N=2e5+100;
using namespace std;
LL a[N],sum[N],c[N],d[N];//c、d两个不同的前缀和
int n;
LL find(int x,LL *array)
{
LL res=0;
for(;x;x-=x&-x)res+=array[x];
return res;
}
void add(int x,int v,LL *array)
{
for(;x<=n;x+=x&-x)array[x]+=v;
}
int main()
{
io;
int m;
while(cin>>n>>m)
{
memset(sum,0,sizeof(sum));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
for(int i=1;i<=n;++i)cin>>a[i];
for(int i=1;i<=n;++i)sum[i]=sum[i-1]+a[i];
while(m--)
{
int x,y,z;
char ch;
cin>>ch;
if(ch=='Q'){
cin>>x>>y;
x=x-1;
LL sum2=sum[y]+(y+1)*find(y,c)-find(y,d);//公式求解
LL sum1=sum[x]+(x+1)*find(x,c)-find(x,d);
cout<<sum2-sum1<<endl;
}
else{
cin>>x>>y>>z; //树状数组区间修改, 差分法,x位置加,y+1位置减
add(x,z,c); //维护两个关于b【】增量数组的前缀和
add(y+1,-z,c);
add(x,(LL)x*z,d);
add(y+1,-(LL)(y+1)*z,d);
}
}
}
return 0;
}
The end;