题目
题目链接
题意
给你个数列,编号为1…n1…n。
给出两种操作:
- 查询操作:查询所有编号模xx得的对应数字之和。
- 修改操作:把编号为xx的数字,修改为。
题解
我们先从最为暴力的思路出发:
- 对于查询xx、来说,我们要统计的就是编号为y,x+y,…,kx+yy,x+y,…,kx+y的数字之和。
我们可以把要求的东西简写成sum[x][y]sum[x][y],代表的含义是模xx余的编号对应的数字之和,下面我们需要来维护这个东西。
预处理:
for(int i = 1;i <= n;++i){for(int x = 1;x <= n;++x){sum[x][i%x] += a[i];}
}
预处理sum[x][y]sum[x][y]的时间复杂度为O(n2)O(n2)
想要降低时间复杂度,我们可以想到分块,分块能降低到O(nn‾√)O(nn)
做法如下:
我们对模数进行分块,当模数x<n‾√x<n的时候,我们暴力与处理,时间复杂度为O(nn‾√)O(nn)。
当模数x>n‾√x>n时候我们不将其处理。
询问模数x<n‾√x<n时候,直接回答。
询问模数x>n‾√x>n时候,暴力按照a[y]+a[x+y]+…+a[kx+y]a[y]+a[x+y]+…+a[kx+y]进行计算,由于x>n‾√x>n,所以求和的数不超过nn‾√nn个。
总的复杂度是x>n‾√x>n。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
#define pr(x) cout<<#x<<":"<<x<<endl
int n,m;
int val[150007];
int dp[1007][1007];
int main(){scanf("%d %d",&n,&m);for(int i = 1;i <= n;++i){int v;scanf("%d",&v);val[i] = v;}for(int i = 1;i <= n;++i){for(int p = 1;p <= 400;++p){dp[p][i%p] += val[i];}}char op;int x,y;for(int i = 0;i < m;++i){scanf(" %c %d %d",&op,&x,&y);if(op == 'A'){if(x <= 400) printf("%d\n",dp[x][y]);else{int ans = 0;for(int j = y;j <= n;j += x){ans += val[j];}printf("%d\n",ans);}}else{for(int p = 1;p <= 400;++p){dp[p][x%p] += y - val[x];}val[x] = y;}}return 0;
}