HDU4267 A Simple Problem with Integers(树状数组)

 

HDU 4267 A Simple Problem with Integers(树状数组)

分析:对于一个特定的k,它有可能有0到k-1 共k个余数.我们对于每条1 a b k c 命令 我们只需要再那些属于[a,b]中的i且i%k==a%k的i上加c即可,所以我们构造树状数组C[k][a%k][MAXN]表示给定k和a 时,对树状数组C[k][a%k][MAXN]执行操作.我们知道我们需要加c的数本来是 a,a+k,a+2*k,…(b/k)*k+a%k.所以我们令C[k][a%k][MAXN]这个树状数组在给定的k和a时,只维护下标是类似:a,a+k,a+2*k,…(b/k)*k+a%k的值.那么这个树状数组的第一个元素即A[k][a%k][1]对应的原数组A[MAXN]的那个下标呢?

K=1时,余数只能为0,所以A[1]代指A[1][0][1].

K=2时,余数为0时,A[2]代指A[2][0][1].

K=2时,余数为1时,A[1]代指A[2][1][1].

那么我们对于命令 1 a b k c,怎么知道a到底是A[k][a%k][MAXN]的第几个数呢?区间[a,b]对A[k][a%k][MAXN]的影响区间是多少呢?

由上面可以看出A[k][a%k][1]的首元素有时候是A[a%k],有时候又是A[a%k+k](因为当a%k=0时,A[0]是无效的).我们不妨对于每次读入的a和b都执行a—和b--,使得整体区间左移一位得:A[0]到A[MAXN-1],执行命令 1 a b k c不会有影响,我们查询的时候照样查找a-1的位置即可.

当执行a—和b—后:

A[k][a%k][MAXN]能管理的元素从小到大依次是:a%k,a%k+k,…a%k+n*k

A[k][a%k][i]元素就是a%k+(i-1)*k,所以(a/k)+1就是a在A[k][a%k][MAXN]中的编号,同样[a,b]能管理到的最后一个可行值的编号为p则: a%k+(p-1)*k<=b< a%k+p*k 推出:

(b-a%k)/k <p<= (b-a%k)/k+1

所以对于命令 1 a b k c,我们先a--,b--,然后对A[k][a%k][MAXN]中的(a/k)+1位置加c,并且(b-a%k)/k +2位置-c.(此处用到了HDU1556的思想:http://blog.csdn.net/u013480600/article/details/21320487 )

当得到命令2 a时:

我们先a--,然后用sum(k,a%k, (a/k)+1)(注意这里K从1到10,要求10次)求出的就是A[a]的当前值.

AC代码:296ms

#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=50000+100;
int c[11][10][MAXN];
int A[MAXN];
int lowbit(int x)
{
    return x&(-x);
}
int sum(int k,int r,int x)
{
    int res=0;
    while(x>0)
    {
        res +=c[k][r][x];
        x-=lowbit(x);
    }
    return res;
}
void add(int k,int r,int x,int v)
{
    while(x<MAXN)
    {
        c[k][r][x]+=v;
        x+=lowbit(x);
    }
}
int main()
{
    int n;
    while(scanf("%d",&n)==1&&n)
    {
        for(int i=0;i<n;i++)
            scanf("%d",&A[i]);
        memset(c,0,sizeof(c));
        int Q;
        scanf("%d",&Q);
        while(Q--)
        {
            int type;
            scanf("%d",&type);
            if(type==1)
            {
                int a,b,k,p;
                scanf("%d%d%d%d",&a,&b,&k,&p);
                a--;
                b--;
                add(k,a%k,a/k+1,p);
                add(k,a%k,(b-a%k)/k+2,-p);
            }
            else if(type==2)
            {
                int a;
                scanf("%d",&a);
                a--;
                int ans=A[a];
                for(int k=1;k<=10;k++)
                    ans+=sum(k,a%k,a/k+1);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}


 

你可能感兴趣的:(ACM)