HDU 4267 A Simple Problem with Integers
这道题可谓经典,树状数组线段树都可以写,在这里总结一下。
题目链接:http://code.hdu.edu.cn/showproblem.php?pid=4267
题意应该是很容易看懂的,所以不解释了。
树状数组法:题目中的方程是: a <= i <= b and (i - a) % k == 0
而 (i - a) % k == 0 转换一下就是 i % k == a % k == mod, 设其值为mod。
K有 1 <= k <= 10 十种情况,则 0<=a%k<=9 至多十种情况。
我们就可以开一个数组tree[50010][11][10],即对11*10棵(实际上是55棵)树状数组进行维护。
每一次数值更新时,我们只需更新tree[0...b][k][a%k],更新量为C。
再更新tree[0...a-1][k][a%k],更新量为-C。
更新函数为void update(int pos, int k, int mod, long long num)。
每次查询的时候,根据给定的位置pos,查询的时候要加和tree[pos][k][pos%k] (1 <= k <= 10)。
每次操作的复杂度都是O(logn),空间也是够用的。
好吧,上代码。
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 #include<queue> 5 #include<math.h> 6 #include<algorithm> 7 #include<string.h> 8 #include<stdlib.h> 9 #include<vector> 10 #include<set> 11 #include<map> 12 13 #define repA(p,q,i) for( int (i)=(p); (i)!=(q); ++(i) ) 14 #define repAE(p,q,i) for( int (i)=(p); (i)<=(q); ++(i) ) 15 #define repD(p,q,i) for( int (i)=(p); (i)!=(q); --(i) ) 16 #define repDE(p,q,i) for( int (i)=(p); (i)>=(q); --(i) ) 17 #define range 50010 18 #define ll long long 19 20 int tree[range][11][10]; 21 //int s[range]; 22 int n; 23 24 void initial(); 25 void update(int pos, int k, int mod, ll num) ; 26 ll query(int a, int v); 27 int lowbit( int i ); 28 29 int main() 30 { 31 int Q,ope,a,b,c,k,pos; 32 while( scanf("%d",&n) != EOF ) 33 { 34 initial(); 35 repAE(1,n,i) scanf("%d",&tree[i][0][0]) ; 36 37 scanf("%d",&Q); 38 while(Q--) 39 { 40 scanf("%d",&ope); 41 if(ope == 1) 42 { 43 scanf("%d%d%d%d",&a,&b,&k,&c); 44 update(b, k, a%k, c); 45 update(a-1, k, a%k, -c); 46 } 47 else if(ope == 2) 48 { 49 scanf("%d",&pos); 50 cout<<query(pos, pos)+tree[pos][0][0]<<endl; 51 } 52 } 53 } 54 //while(1); 55 return 0; 56 } 57 58 void initial() 59 { 60 repAE(0,n,i) 61 repAE(0,10,j) 62 repAE(0,9,k) 63 tree[i][j][k]=0; 64 return ; 65 } 66 67 int lowbit( int i ) 68 { 69 return i&(-i) ; 70 } 71 72 void update(int pos, int k, int mod, ll num) 73 { 74 while( pos > 0 ) 75 { 76 tree[pos][k][mod] += num ; 77 pos -= lowbit(pos) ; 78 } 79 return ; 80 } 81 82 ll query(int a, int v) 83 { 84 ll sum=0; 85 while(v<=n) 86 { 87 repAE(1,10,i) 88 sum += tree[v][i][a%i] ; 89 v += lowbit(v) ; //筒子们这里注意啦! 90 //v[i]的更新信息是保存在v[i] 91 //及所有能管辖v[i]的元素里面 92 //所以需要对这些元素进行加和 93 } 94 return sum; 95 }
线段树法:对于这道题来说,线段树法显然会更快。
因为树状数组法中每次更新操作对数组更新了两次,也就是说运行时间几乎是线段树的两倍。
HDU上用C++提交实测树状数组法406ms,线段树250ms,但是线段树对空间要求显然更大,所以敲代码的时候要卡一卡内存。
下面解释一下思路,其实和树状数组差不多。
先上结点:
struct node{
int left,right,mid;
int add[57]; //add[]的作用和树状数组中的tree[i][11][10]是一样的,只是这里为了照顾内存,就卡紧了开。
};
把区间[a,b]的更新信息储存在add[]中,对应树状数组中tree[pos][k][pos%k],公式是add[( k*(k-1) )/2+mod] 。
学过线段树的人都不用再解释了,上代码。
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 #include<queue> 5 #include<math.h> 6 #include<algorithm> 7 #include<string.h> 8 #include<stdlib.h> 9 #include<vector> 10 #include<set> 11 #include<map> 12 13 #define repA(p,q,i) for( int (i)=(p); (i)!=(q); ++(i) ) 14 #define repAE(p,q,i) for( int (i)=(p); (i)<=(q); ++(i) ) 15 #define repD(p,q,i) for( int (i)=(p); (i)!=(q); --(i) ) 16 #define repDE(p,q,i) for( int (i)=(p); (i)>=(q); --(i) ) 17 #define range 50005 18 #define ll long long 19 20 struct node{ 21 int left,right,mid; 22 int add[57]; 23 }; 24 25 node segment[3*range]; 26 int arr[range]; 27 int n,Q,a,b,c,k,ope,pos; 28 29 void update(int k, int mod, int l, int r, int c, int num); 30 void made(int l, int r, int num); 31 ll query( int num, int pos ); 32 33 int main() 34 { 35 while( scanf("%d",&n) != EOF ) 36 { 37 made(0,n+1,1); 38 repAE(1,n,i) scanf("%d",&arr[i]); 39 scanf("%d",&Q); 40 while(Q--) 41 { 42 scanf("%d",&ope); 43 if(ope == 1) 44 { 45 scanf("%d%d%d%d",&a,&b,&k,&c); 46 update(k, a%k, a, b+1, c, 1); 47 } 48 else if(ope == 2) 49 { 50 scanf("%d",&pos); 51 cout<< query(1,pos)+arr[pos] <<endl; 52 } 53 } 54 } 55 return 0; 56 } 57 58 void made(int l, int r, int num) 59 { 60 segment[num].left = l ; 61 segment[num].right = r ; 62 segment[num].mid = l + (r-l)/2 ; 63 repA(0,57,i) 64 segment[num].add[i] = 0 ; 65 if( 1+l != r ) 66 { //递归地建左右树 67 made(l, segment[num].mid, 2*num) ; 68 made(segment[num].mid, r, 2*num+1) ; 69 } 70 } 71 72 void update(int k, int mod, int l, int r, int c, int num) 73 { 74 if(segment[num].left == l && segment[num].right == r) 75 { 76 segment[num].add[( k*(k-1) )/2+mod] += c ; 77 return ; 78 } 79 if(r <= segment[num].mid) 80 update(k, mod, l, r, c, 2*num); 81 else if(l >= segment[num].mid) 82 update(k, mod, l, r, c, 2*num+1); 83 else 84 { 85 update(k, mod, l, segment[num].mid, c, 2*num); 86 update(k, mod, segment[num].mid, r, c, 2*num+1); 87 } 88 return ; 89 } 90 91 ll query( int num, int pos ) 92 { 93 ll all=0; 94 if( segment[num].left <= pos && segment[num].right >=pos+1 ) 95 { 96 repAE(1,10,i) 97 all += segment[num].add[( i*(i-1) )/2+pos%i] ; 98 } 99 if( segment[num].left == pos && segment[num].right ==pos+1 ) 100 return all; 101 if( pos+1 <= segment[num].mid ) 102 all += query(2*num, pos) ; 103 else if( pos >= segment[num].mid ) 104 all += query(2*num+1, pos) ; 105 return all; 106 }