题目大意:
给定一个一段区间,让你执行两种操作,一种是加入一种是查询。
题解:根据k的值建立10类树状数组,每类中根据i%k的不同建立k棵树状数组,也就是55棵树状数组,这样每次修改操作只对其中1棵树状数组进行操作,所以是O(logN)的复杂度,每次查询只对其中10棵树状数组统计增量和,所以是O(10*logN)的复杂度。
55棵:k取1的mod有1个为0 k取2的mod有2个为1,0 以此类推,k取10的时候mod有0,1,2,3,4,5,6,7,8,9十个 所以共55个
取树状数组的in[100][MAXD] 对于每一个k取数组的10位开始
#include<iostream> #define MAXD 50010 using namespace std; int n,m,in[100][MAXD],a[MAXD]; int lowbit(int t)//求最小幂2^k { return t & (t^(t-1)); } void insert(int k,int pos,int num) { while(pos<=n) { in[k][pos]+=num; pos+=lowbit(pos); } } int query(int id) { int i, k, x, ans = 0; //从余数1开始扫到10 for(i = 1; i <= 10; ++i ) { //对于每类k有i%k的类 k = (i - 1) * 10 + id % i; for(x = id; x > 0; x -= x & -x) ans += in[k][x]; } return a[id] + ans; } void solve() { int i, op, a, b, k, c; scanf("%d", &m); for(i = 0; i < m; ++i ) { scanf("%d", &op); if(op == 1) { scanf("%d%d%d%d", &a, &b, &k, &c); //优化,因为实际取莫是因为a一直到a+nk,使得b为最后一个可以被余数取的数 b -= (b - a) % k; //对于每个k从数组10开始记录k的余数(例如k=4那么k的余数要0,1,2,3但是起始位置是30的下标开始) insert(10 * (k - 1) + a % k, a, c); /*树状数组成段更新的弊端因为上面的insert相当于将a->∞都赋值为c 那么后面我将b+1->∞赋值为-c那么就相当于将a->b赋值为c*/ insert(10 * (k - 1) + b % k, b + 1, -c); } else if(op == 2) { scanf("%d", &a); printf("%d\n", query(a)); } } } void init() { int i, j; for(i = 1; i <= n; ++i ) scanf("%d", &a[i]); memset(in,0,sizeof(in)); } int main() { while(~scanf("%d", &n)) { init(); solve(); } return 0; }