POJ3468--A Simple Problem with Integers--线段树/树状数组 改段求段
2016-09-21 20:54
281 查看
题目描述
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.
解题思路
不同于改段求点或改点求段,这是一个改段求段的题目有两种方法接题:
第一种为利用树状数组:
区间更新这里引进了一个数组delta数组,delta[i]表示区间 [i, n] 的共同增量,每次你需要更新的时候只需要更新delta数组就行了,因为每段区间更新的数都记录在这个数组
中,那怎么查询前缀和呐?
sum[i]=a[1]+a[2]+a[3]+......+a[i]+delta[1](i-0)+delta[2](i-1)+delta[3](i-2)+......+delta[i](i-i+1);
= sigma( a[x] ) + sigma( delta[x] * (i + 1 - x) )
= sigma( a[x] ) + (i + 1) * sigma( delta[x] ) - sigma( delta[x] * x )
(引用自吕程博客)
具体代码为
#include<iostream> #include<string.h> #include<stdio.h> using namespace std; const int M=100010; long long add_sum[M]; long long add[M]; long long ori[M]; int MN; int Q; int lowbit(int x) { return x&(-x); } void update(int i,int v,long long* a) { for(;i<=MN;i+=lowbit(i)){ a[i]+=v; } } long long getsum(int r,long long *a) { long long resr=0; for(;r>0;r-=lowbit(r)){ resr+=a[r]; } return resr; } int main() { //freopen("data.in","r",stdin); std::ios::sync_with_stdio(false); std::cin.tie(0); int tem; int l,r,val; while(cin>>MN>>Q){ memset(ori,0,sizeof(ori)); memset(add_sum,0,sizeof(add_sum)); memset(add,0,sizeof(add)); for(int i=1;i<=MN;i++){ cin>>tem; update(i,tem,ori); } string ch; for(int i=0;i<Q;i++){ cin>>ch; if(ch=="Q"){ cin>>l>>r; cout<<getsum(r,ori)-getsum(l-1,ori)-l*getsum(l-1,add)+(r+1)*getsum(r,add)-getsum(r,add_sum)+getsum(l-1,add_sum)<<endl; } if(ch=="C"){ cin>>l>>r>>val; update(l,val,add); update(r+1,-val,add); update(l,val*(l),add_sum); update(r+1,-val*(r+1),add_sum); } } } }
注意:add_sum数组保存的是add[i]*i之后的值
第二种方法为利用线段树
可以写一个利用结构体实现的线段树,然后加懒标。我地AC代码是按照刘汝佳的板写的,我觉得他地方法比较简洁,更好玩
下面是代码(详解见算法竞赛入门经典-训练指南):
#include<iostream> #include<string.h> #include<string> #include<stdio.h> #include<stdlib.h> using namespace std; const int M = 4*100010; long long sumv[M];//线段树sum数组 long long add[M]; long long x, y;//全局变量,update与query时使用 long long addval; long long _sum; //*********************// void update(int id, int l, int r) { long long m = l + (r - l) / 2; if (x <= l&&y >= r){ add[id] += addval; } else{ if (x <= m) update(2 * id, l, m); if (y>m) update(2 * id + 1, m+1, r); } sumv[id]=0;//!!!!!!!!!注意此步骤 if (l<r){ sumv[id] = sumv[2 * id] + sumv[2 * id + 1]; } sumv[id] += add[id] * (r - l + 1); } void query(int id, int l, int r, long long addv) { long long m = l + (r - l) / 2; if (x <= l&&y >= r){ _sum += sumv[id] + addv*(r - l + 1); } else{ //addv += add[id]; if (x <= m){ query(2 * id, l, m, addv+add[id]); } if (y>m) query(2 * id + 1, m+1, r, addv+add[id]); } } int main() { //freopen("data.in", "r", stdin); //freopen("data.out","w",stdout); std::ios::sync_with_stdio(false); std::cin.tie(0); long long n, q; while (cin >> n >> q){ memset(sumv, 0, sizeof(sumv)); memset(add, 0, sizeof(add)); for (int i = 1; i <= n; i++){ cin >> addval; x = i; y = i; update(1, 1, n); } string ch; for (int i = 0; i<q; i++){ cin >> ch; if (ch == "Q"){ _sum = 0; cin >> x >> y; query(1, 1, n, 0); cout << _sum << endl; } if (ch == "C"){ cin >> x >> y >> addval; update(1, 1, n); } } } }
相关文章推荐
- POJ3468:A Simple Problem with Integers (线段树||树状数组||Splay解决基本问题的效率对比)
- POJ3468_A Simple Problem with Integers_树状数组区间和::区间修改
- POJ3468 A Simple Problem With Integers 树状数组 区间更新区间询问
- 模板(线段树 + 树状数组 + 区间修改 + 区间查询)eg:POJ 3468 - A Simple Problem with Integers
- poj-3468 A Simple Problem with Integers(线段树,树状数组区间求和)
- poj 3468 A Simple Problem with Integers 树状数组 或 线段树
- poj 3468 A Simple Problem with Integers(线段树区间更新 or 树状数组区间更新)
- poj3468--A Simple Problem with Integers(树状数组解法)
- 【POJ3468】【树状数组区间修改】A Simple Problem with Integers
- POJ3468 A Simple Problem with Integers(树状数组->改段求点)
- POJ 3468 A Simple Problem with Integers【线段树/树状数组】
- poj 3468 A Simple Problem with Integers (线段树区间更新 + 树状数组区间更新)
- [树状数组] poj3468 A Simple Problem with Integers
- poj3468- A Simple Problem with Integers-解题报告-线段树
- POJ3468 A Simple Problem with Integers 线段树 区间成段更新+区间求和
- POJ3468 A Simple Problem with Integers(线段树延时标记)
- POJ 3468 A Simple Problem with Integers (树状数组写法)
- 【北大夏令营笔记-线段树】POJ3468-A Simple Problem with Integers
- poj3468_A Simple Problem with Integers_线段树_成段更新求和
- poj3468 A Simple Problem with Integers(线段树模板 功能:区间增减,区间求和)