A Simple Problem with Integers POJ - 3468
原题链接
考察:树状数组+差分
上一道题的加强版,但还是结合差分数组
思路:
操作一:"C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。
这里还是得用到差分,设b数组为原数组的差分数组,那么此操作就转化为单点修改
操作二:"Q a b" 询问[a, b]区间中所有值的和。
这里就不是差分数组了,而是原数组的前缀和.但是如果我们同时维护两个数组:差分数组和原数组是不可能的,这两个操作在差分数组和原数组必然有时间复杂度的冲突.因此考虑其他做法.
我们不考虑[l,r]区间的和,直接考虑前缀和更为方便.那么就是求\(\sum_{i=1}^r\) \(\sum_{j=1}^i\)b[j] = \(\sum_{i=1}^r\)a[i]
而这个式子等价于 \(\sum_{i=1}^r\)(r-i+1)b[i].
这个式子很难求前缀和,因为r与i都是不确定的数.而树状数组只能维护b[i],b[i]+c,b[i]*c这种已确定的常量的前缀和.
但如果我们把(r+1)b[i]提取出来,即(r+1)b[i]-ib[i]的前缀和.每次操作r都是常数,i是在变化的,所以还需要维护一个i*b[i]的前缀和.
注意一下long long
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 100010,M = 3;//维护b[i],i*b[i]
int n,m;
LL tr[2][N],b[N];
char s[M];
void insert(int l,int r,int x)
{
b[l]+=x,b[r+1]-=x;
}
int lowbit(int x)
{
return x&-x;
}
void add(int k,int idx,LL x)
{
for(int i=idx;i<=n;i+=lowbit(i)) tr[k][i]+=x;
}
LL ask(int k,int r)
{
LL res = 0;
for(int i=r;i;i-=lowbit(i)) res+=tr[k][i];
return res;
}
LL get(int l,int r)
{
LL a = (r+1ll)*ask(0,r)-ask(1,r);
LL b = l*ask(0,l-1)-ask(1,l-1);
return a-b;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int x; scanf("%d",&x);
insert(i,i,x);
}
for(int i=1;i<=n;i++)
add(0,i,b[i]),add(1,i,(LL)i*b[i]);
while(m--)
{
int r,l,x;
scanf("%s%d%d",s,&l,&r);
if(s[0]=='Q')
{
printf("%lld\n",get(l,r));
continue;
}
scanf("%d",&x);
add(0,l,x); add(0,r+1,-x);
add(1,l,(LL)x*l); add(1,r+1,-(LL)x*(r+1));
}
return 0;
}
到这里可以再补一个解法二.即线段树的区间修改.涉及懒标记.懒标记是标记当前结点的子节点的值需要+tag.
我们只要三个地方需要加懒标记.
- 修改区间时,如果当前区间完全被修改区间覆盖,则需要加懒标记
- 修改区间时,当前区间没有被完全覆盖.这时需要修改子结点区间的值再给子节点加懒标记.懒标记的作用范围是整个区间.不允许部分+-x的情况存在.
- 查询区间时,当前区间没有被完全覆盖.同上步骤.
以下为code:
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 100010;
int n,m,a[N];
char s[3];
struct Node{
int l,r;
LL val,tag;
}tr[N<<2];
void push_up(int u)
{
tr[u].val = tr[u<<1].val+tr[u<<1|1].val;
}
void push_down(int u)
{
LL& tag = tr[u].tag;
if(!tag) return;
tr[u<<1].tag +=tag;
tr[u<<1].val += (tr[u<<1].r-tr[u<<1].l+1)*tag;
tr[u<<1|1].tag += tag;
tr[u<<1|1].val += (tr[u<<1|1].r-tr[u<<1|1].l+1)*tag;
tr[u].tag = 0;
}
void build(int u,int l,int r)
{
tr[u].l = l,tr[u].r = r;
if(l==r) {tr[u] = {l,r,a[l],0}; return;}
int mid = l+r>>1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
push_up(u);
}
void modify(int u,int l,int r,int x)
{
if(tr[u].l>=l&&tr[u].r<=r)
{
tr[u].val += (LL)(tr[u].r-tr[u].l+1)*x;
tr[u].tag+=x;
return;
}
push_down(u);//add 全部区间10与add 部分区间5不能混合
int mid = tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,x);
if(mid<r) modify(u<<1|1,l,r,x);
push_up(u);
}
LL query(int u,int l,int r)
{
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].val;
int mid = tr[u].l+tr[u].r>>1;
push_down(u);
LL res = 0;
if(l<=mid) res+=query(u<<1,l,r);
if(mid<r) res+=query(u<<1|1,l,r);
return res;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
int l,r; LL d;
scanf("%s%d%d",s,&l,&r);
if(s[0]=='Q') {printf("%lld\n",query(1,l,r));continue;}
scanf("%lld",&d);
modify(1,l,r,d);
}
return 0;
}