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 }
线段树法

 

 

       

       

 

posted on 2013-09-12 17:03  码农之上~  阅读(177)  评论(0编辑  收藏  举报

导航