A Simple Problem with Integers(POJ 3468)
- 原题如下:
A Simple Problem with Integers
Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 142057 Accepted: 44088 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. - 题解1:先考虑利用线段树,每个节点维护两个数据:①给这个节点对应的区间内的所有元素共同加上的值 ②在这个节点对应的区间中除去①之外其它的值的和。通过单独维护共同加上的值,给区间同时加一个值的操作就可以高效地进行了,如果对于父亲节点同时加了一个值,那么这个值就不会在儿子节点被重复考虑。在递归计算和时再把这一部分的值加到结果里面就可以了。这样不论是同时加一个值还是查询一段的和复杂度都是O(log n).
- 代码1:
1 #include <cstdio> 2 #include <cctype> 3 #include <cmath> 4 #define number s-'0' 5 6 using namespace std; 7 8 const int MAX_N=100000; 9 const int MAX_Q=100000; 10 const int DAT_SIZE=(1<<18)-1; 11 int N,Q; 12 int A[MAX_N]; 13 char T[MAX_Q]; 14 int L[MAX_Q], R[MAX_Q], X[MAX_Q]; 15 long long data[DAT_SIZE], datb[DAT_SIZE]; 16 17 void read(int &x){ 18 char s; 19 x=0; 20 bool flag=0; 21 while(!isdigit(s=getchar())) 22 (s=='-')&&(flag=true); 23 for(x=number;isdigit(s=getchar());x=x*10+number); 24 (flag)&&(x=-x); 25 } 26 27 void write(int x) 28 { 29 if(x<0) 30 { 31 putchar('-'); 32 x=-x; 33 } 34 if(x>9) 35 write(x/10); 36 putchar(x%10+'0'); 37 } 38 39 int min(int x, int y) 40 { 41 if (x<y) return x; 42 return y; 43 } 44 45 int max(int x, int y) 46 { 47 if (x>y) return x; 48 return y; 49 } 50 51 void solve(); 52 void add(int a, int b, int x, int k, int l, int r); 53 long long sum(int a, int b, int k, int l, int r); 54 55 int main() 56 { 57 read(N);read(Q); 58 for (int i=0; i<N; i++) read(A[i]); 59 for (int i=0; i<Q; i++) 60 { 61 scanf("%c", &T[i]); 62 if (T[i]=='Q') {read(L[i]);read(R[i]);} 63 else {read(L[i]);read(R[i]);read(X[i]);} 64 } 65 solve(); 66 } 67 68 void solve() 69 { 70 for (int i=0; i<N; i++) add(i, i+1, A[i], 0, 0, N); 71 for (int i=0; i<Q; i++) 72 if (T[i]=='C') add(L[i]-1, R[i], X[i], 0, 0, N); 73 else printf("%lld\n", sum(L[i]-1, R[i], 0, 0, N)); 74 } 75 76 void add(int a, int b, int x, int k, int l, int r) 77 { 78 if (a<=l && r<=b) data[k]+=x; 79 else if (l<b && r>a) 80 { 81 datb[k]+=(min(b,r)-max(a,l))*x; 82 add(a, b, x, k*2+1, l, (l+r)/2); 83 add(a, b, x, k*2+2, (l+r)/2, r); 84 } 85 } 86 87 long long sum(int a, int b, int k, int l, int r) 88 { 89 if (b<=l || a>=r) return 0; 90 else if(l>=a && r<=b) return data[k]*(r-l)+datb[k]; 91 else 92 { 93 long long res=(min(b,r)-max(a,l))*data[k]; 94 res+=sum(a, b, k*2+1, l, (l+r)/2); 95 res+=sum(a, b, k*2+2, (l+r)/2, r); 96 return res; 97 } 98 }
- 题解2:树状数组也可以通过在每个节点上维护两个数据,高效地进行上述操作。如果给区间[l,r]同时加上x的话,考虑每个节点的值会如何变化。令s(i)是加上x之前的前缀和,s'(i)是加上x之后的前缀和。那么就有:
i<l→s'(i)=s(i)
l≤i≤r→s'(i)=s(i)+x*(i-l+1)
=s(i)+x*i-x*(l-1)
r<i→s'(i)=s(i)+x*(r-l+1)
下面记sum(bit,i)为树状数组bit的前i项和。我们构建两个树状数组bit0和bit1,并且设∑(j=1,i)aj=sum(bit1,i)*i+sum(bit0,i)
那么在[l,r]区间上同时加上x就可以看成是
1.在bit0的l位置加上-x(l-1)
2.在bit1的l位置加上x
3.在bit0的r+1位置加上xr
4.在bit1的r+1位置加上-x
这4个操作。因此查询和更新操作都可以在O(log n)时间里完成。
更一般地,如果操作得到的结果可以用i的n次多项式表示,那么就可以使用n+1个树状数组来进行维护了。 - 代码2:
1 #include <cstdio> 2 #include <cctype> 3 #include <cmath> 4 #define number s-'0' 5 6 using namespace std; 7 8 const int MAX_N=100000; 9 const int MAX_Q=100000; 10 int N,Q; 11 int A[MAX_N+1]; 12 char T[MAX_Q]; 13 int L[MAX_Q], R[MAX_Q], X[MAX_Q]; 14 long long bit0[MAX_N+1], bit1[MAX_N+1]; 15 16 17 void read(int &x){ 18 char s; 19 x=0; 20 bool flag=0; 21 while(!isdigit(s=getchar())) 22 (s=='-')&&(flag=true); 23 for(x=number;isdigit(s=getchar());x=x*10+number); 24 (flag)&&(x=-x); 25 } 26 27 void write(long long x) 28 { 29 if(x<0) 30 { 31 putchar('-'); 32 x=-x; 33 } 34 if(x>9) 35 write(x/10); 36 putchar(x%10+'0'); 37 } 38 39 long long sum(long long *b, int i) 40 { 41 long long s=0; 42 while (i>0) 43 { 44 s+=b[i]; 45 i=i&(i-1); 46 } 47 return s; 48 } 49 50 void add(long long *b, int i, int v) 51 { 52 while (i<=N) 53 { 54 b[i]+=v; 55 i+=i&-i; 56 } 57 } 58 59 void solve(); 60 61 int main() 62 { 63 read(N);read(Q); 64 for (int i=1; i<=N; i++) read(A[i]); 65 for (int i=0; i<Q; i++) 66 { 67 scanf("%c", &T[i]); 68 if (T[i]=='Q') {read(L[i]);read(R[i]);} 69 else {read(L[i]);read(R[i]);read(X[i]);} 70 } 71 solve(); 72 } 73 74 void solve() 75 { 76 for (int i=1; i<=N; i++) 77 { 78 add(bit0, i, A[i]); 79 } 80 for (int i=0; i<Q; i++) 81 { 82 if (T[i]=='C') 83 { 84 add(bit0, L[i], -X[i]*(L[i]-1)); 85 add(bit1, L[i], X[i]); 86 add(bit0, R[i]+1, X[i]*R[i]); 87 add(bit1, R[i]+1, -X[i]); 88 } 89 else 90 { 91 long long res=0; 92 res+=sum(bit0, R[i])+sum(bit1, R[i])*R[i]; 93 res-=sum(bit0, L[i]-1)+sum(bit1, L[i]-1)*(L[i]-1); 94 printf("%lld\n", res); 95 } 96 } 97 }