HDU 4267 (树状数组)

来源:互联网 发布:php extension dir 编辑:程序博客网 时间:2024/05/16 06:45

A Simple Problem with Integers

Time Limit: 5000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 5833    Accepted Submission(s): 1871

Problem Description

Let A1, A2, ... , AN be N elements. You need to deal with two kinds of operations. One type of operation is to add a given number to a few numbers in a given interval. The other is to query the value of some element.

Input

There are a lot of test cases.
The first line contains an integer N. (1 <= N <= 50000)
The second line contains N numbers which are the initial values of A1, A2, ... , AN. (-10,000,000 <= the initial value of Ai <= 10,000,000)
The third line contains an integer Q. (1 <= Q <= 50000)
Each of the following Q lines represents an operation.
"1 a b k c" means adding c to each of Ai which satisfies a <= i <= b and (i - a) % k == 0. (1 <= a <= b <= N, 1 <= k <= 10, -1,000 <= c <= 1,000)
"2 a" means querying the value of Aa. (1 <= a <= N)

Output

For each test case, output several lines to answer all query operations.

Sample Input

4 1 1 1 1142 12 22 32 41 2 3 1 22 1 2 22 32 41 1 4 2 12 12 22 32 4

Sample Output

111113312341

Source

2012 ACM/ICPC Asia Regional Changchun Online



        又是树状数组的专题,好多都做过,以为没有什么能够难倒我,然而还是有些题目虽然做出来了但是废了点周折。

        这题的话,就是区间修改单点查询,但是不同之处在于,在一个区间内,他不是全部都修改,而是隔几个的修改。例如说,当k=2的时候,对于区间[l,r],修改l、l+2,l+4,……然后k是给定的,满足1 <= k <= 10。

        初看没有什么思路,直接全部修改肯定不行,暴力修改肯定超时。但是很快萌生一个思路:既然k的取值比较小,我可以进行分组,令每一个k的取值为一组,然后建立树状数组。在去吃饭的路上,有了大体的想法,每一组建立的树状数组设置为二维,以k=10的组为例,按顺序每行放十个数字,这样对于一个修改操作[l,r],我们修改的是l、l+2,l+4……相当于这个二维树状数组中l所在的列。

        乍一看,这个方法似乎可行,但是回来之后发现实现起来并不简单。于是便摒弃这个想法,把分组变多,然后每个组建立一个一维的树状数组。注意到当k=1时,仅有一种分组;当k=2时,有两种分组,分别是以1和2为开始的两组;当k=3的时,有3种分组,分别是以1、2和3为开始的两组……这样总共有55个分组。为了表示的方便,我建立一个树状数组类型的二维数组BIT[k][i],表示间隔k且以i为开头的组的树状数组。不难发现,这样子分组之后,每一个确定的数字i的最后取值就取决于与之相关的10个树状数组。我们在修改的时候,只修改对应的一棵树状数组,然后查询的时候把相关点相关的10个树状数组的值相加并加上初始值即可。

        另外,注意所有的树状数组所保存的值都是改变量,所以最后要加上初始值,并不需要把初始值初始化给每一个树状数组。还有一点,就是分组之后,我并没有把组内的数字重标号,即组内的有效点是离散的,例如一个组:1、3、5、7,如果修改[1,5],实际上是1~5包括偶数都修改的,可能会觉得偶数修改会错,但是实际上分组之后真正有效的还是只有1、3、5、7,即即使修改了偶数也不会对结果产生影响。这个具体理解一下我的问答的操作就会知道。具体代码如下:

#include<iostream>#include<cstdio>#include<algorithm>#include<cstdlib>#include<cmath>#include<cstring>#include<iomanip>#define N 50100using namespace std;int n,a[N];struct DifferenceBIT{int c[N];inline int lowbit(int x){return x&-x;}inline void updata(int x,int k){while (x<=n){c[x]+=k;x+=lowbit(x);}}inline int getsum(int x){int res=0;while (x>0){res+=c[x];x-=lowbit(x);}return res;}} BIT[12][12];int getsum(int x){    int res=0;    for(int k=1;k<=10;k++)        res+=BIT[k][x%k].getsum(x);//只加上真实含有x的组的树状数组值    return res;}int main(){    while (~scanf("%d",&n))    {        memset(BIT,0,sizeof(BIT));        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        int q; scanf("%d",&q);        while (q--)        {            int op; scanf("%d",&op);            if (op==2)            {                int x; scanf("%d",&x);                printf("%d\n",getsum(x)+a[x]);            } else            {                int l,r,k,x;                scanf("%d%d%d%d",&l,&r,&k,&x);                BIT[k][l%k].updata(l,x);                BIT[k][l%k].updata(r+1,-x);            }        }    }    return 0;}

原创粉丝点击