您的位置:首页 > 其它

POJ 3468 A Simple Problem with Integers(树状数组区间更新)

2015-11-07 14:11 423 查看
今天来学习一下怎么用BIT区间更新的,BIT速度比线段树速度更快,也更好写。

我们来看一下当给区间[l,r]整体加上一个常数c会前缀si发生什么变化?

i < l ,si不变; l ≤ i ≤ r,si 增加 了c*(i-l+1); r < i,si 增加了c*(r-l+1)。

如果画出增量与下标的坐标图,我们可以发现两边增加的都是常量,而中间的散点在一条斜率为c的直线上。

因此我们可以用一个斜率和常数的组合来表示前缀和,si = b1 *i + b2。

那么给区间[l,r]加上c可以表示为:

1. [l,r]上每个结点的斜率加上c,b1上l位置加c,r+1位置减c。

2.虽然步骤1更新了[l,r]的斜率,但是增量却往上平移了c*(l-1),因此在b2上l位置减掉c*(l-1)。

3.最后更新r < i之后的常数,在b2上r+1位置加上掉c*(r)。

运行效率对比: 未加I/O挂

BIT 1891ms,线段树(适当维护信息) 2349ms , 线段树(懒操作) 2500ms 。

/*********************************************************
*            ------------------                          *
*   author AbyssalFish                                   *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
using namespace std;

typedef long long ll;
const int maxn = 1e5+1;
ll C[2][maxn];
ll psum[maxn];
int N;
inline void add(ll C[],int x,int d)
{
while(x <= N){
C[x] += d;
x += x&-x;
}
}

inline ll sum(ll C[],int x)
{
ll ret = 0;
while(x > 0){
ret += C[x];
x &= x-1;
}
return ret;
}

inline ll query(int x){ return sum(C[1],x)*x+sum(C[0],x); }

//#define LOCAL
int main()
{
#ifdef LOCAL
freopen("in.txt","r",stdin);
#endif
int Q; scanf("%d%d",&N,&Q);
for(int i = 1; i <= N; i++){
scanf("%I64d", psum + i);
psum[i] += psum[i-1];
}
char op[2];
while(Q--){
int l,r; scanf("%s%d%d",op,&l,&r);
if(*op == 'Q'){
printf("%I64d\n",query(r)-query(l-1)+psum[r]-psum[l-1]);
}else {
int c; scanf("%d",&c);
add(C[1],l,c);
add(C[1],r+1,-c);
add(C[0],l,c*(1-l));
add(C[0],r+1,c*(r));
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: