标签:
今天来学习一下怎么用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; }
POJ 3468 A Simple Problem with Integers
标签:
原文地址:http://www.cnblogs.com/jerryRey/p/4944959.html