树状数组区间修改 neu1454

树状数组不止可以单点修改也可以进行区间修改

但是要建立两个树状数组进行维护

                第一个树状数组B[i]存储的是 就是i所表示的区间上所有数字都被加了多少。比如B[i]=v,表示i区间上每个数都加上了v。

                因为树状数组只能够修改区间1-x,加上v(v可能为负数)

                所以第二个树状数组C[i]存储的是区间修改[1-x](x>=i)对于区间i的影响。比如现在修改区间[1-x],如果x<i,那么不对C[i]操作,如果i>=x,那么C[i]+=x*v;

下面是我的模板:

int ADD_B(int x, int v){
     for (int i=x; i>0; i-=i&(-i)) B[i]+= v;
     return 0;
}
int ADD_C(int x, int v){
     for (int i=x; i<=n; i+=i&(-i))  C[i]+=c*x;
     return 0;
}

这里是修改操作。

int SUM_B(int x){
    int ans = 0;
    for (int i=x; i<=n; i+=i&(-i)) s += B[i];
    return ans;
}
int SUM_C(int x){
    int ans = 0;
    for (int i=x; i>0; i-=i&(-i)) ans += C[i];
    return ans;
}
这里是查询操作,但是记住B与C所形成的树状数组都不是很完全,所以还要进行一下操作。

int SUM(int x){
    if (x)return SUM_B(x)*x+SUM_C(x-1);
    return 0;
}

至于为什么这样写,对应上面C树状数组与B树状数组的定义。

修改区间[l,r] +v

ADD_B(r,v);
ADD_C(r,V);
if (l>1) ADD_B(l-1,-v),ADD_C(l-1,-v);

查询区间[l,r] 

int ans;
if (l>1) ans=SUM(r)-SUM(l-1);
else ans=SUM(r);



1454: 逃票的chanming(1

时间限制: 1 Sec   内存限制: 128 MB
提交: 402   解决: 15
[ 提交][ 状态][ 讨论版]

题目描述

这是一个神奇的国度。
    这个国度一共有N个城市组成,让我们将他们编号为1~N,
    这一天,chanming带着他的第一个月的工资K元来到了城市1。他想到城市N去寻找宝藏。qinhang3想跟chanming一起去,但是chanming并不想带他(太大只,逃票太明显)。于是他对qinhang3说,这样吧,你帮我解决这个问题,我们就一起走,不然就只能友尽了,这个题目是这样的:
    有一个长度为N的序列,初始值都为0。
    现在有2种不同操作:
    操作0: 读入p,q,v,并且a[p] ^= v, a[p + 1] ^= v, .. ,a[q] ^= v;
    操作1:  读入p,q,输出s = a[p] ^ a[p + 1] ^ a[p + 2]..^a[q]的结果;
    *@.@*...还好qinhang3找到了一位神犇帮忙,不用东张西望了,就是你!如果你能解决这个问题的话,qinhang3将送给你一个精美的气球作为报答。

输入

单组数据
第一行为两个数N,M,分别表示序列的个数和操作的个数
接下来M行,第一个数op,表示操作的种类,如果为0,读入p,q,v,如果为1,读入p,q
N <= 500000
M <= 500000
0 < v < 2^30

输出

对于每次操作1,输出计算结果。

样例输入

100 3
0 2 6 3
0 1 2 7
1 2 3

样例输出

7

提示

 由于涉及大规模输入输出,请使用 SCANFPRINTF来避免TLE..

这道题不能够用线段树做,做完各种TLE。只能够用树状数组做。

我的代码:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int C[500005];
int B[500005];
int n,m;
void ADD_B(int x, int c)
{
     for (int i=x; i>0; i-=i&(-i)) B[i]^= c;
}
void ADD_C(int x, int c)
{
     for (int i=x; i<=n; i+=i&(-i)) if (x%2) C[i]^=c;
}
int SUM_B(int x)
{
    int s = 0;
    for (int i=x; i<=n; i+=i&(-i)) s ^= B[i];
    return s;
}
int SUM_C(int x)
{
    int s = 0;
    for (int i=x; i>0; i-=i&(-i)) s ^= C[i];
    return s;
}
inline int SUM(int x)
{
    if (x%2) return SUM_B(x)^SUM_C(x - 1);
    else if (x!=0) return  SUM_C(x-1);
    else return 0;
}
int main (){
    int a,l,r,v;
    while(scanf("%d%d",&n,&m)==2) {
        memset(C,0,sizeof(C));
        for(int i=0;i<m;i++) {
            scanf("%d",&a);
            if(a==0) {
                scanf("%d%d%d",&l,&r,&v);
                if (l>1){
                    ADD_B(l-1,v);
                    ADD_C(l-1,v);
                }
                ADD_B(r,v);
                ADD_C(r,v);
            }
            else {
                scanf("%d%d",&l,&r);
                int ans=SUM(l-1)^SUM(r);
                //cout <<sum(l-1)<<endl;
                //cout <<sum(r)<<endl;
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}









  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值