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; }