题目大意:
题目链接:https://www.luogu.org/problemnew/show/P3396
给出一个数列,其中第
i
i
i个数在
m
o
d
p
mod\ p
mod p下会被装进第
i
m
o
d
p
i\ mod\ p
i mod p个哈希池。维护一下两种操作:
- A p x A\ p\ x A p x:询问在 m o d p mod\ p mod p下池 x x x内的数字之和
- C x y C\ x\ y C x y:将第 x x x个数字改成 y y y
思路:
根号算法好题。
应该不算分块吧。。。
对于这道题,我们分开两段维护:
- 模数 ≤ T \leq T ≤T。此时我们预处理出 c n t [ p ] [ x ] cnt[p][x] cnt[p][x]表示在模 p p p下池 x x x的元素之和。每次询问 O ( 1 ) O(1) O(1),预处理 O ( T ) O(T) O(T)。
- 模数 > T >T >T。此时我们只要从 x x x开始,暴力求答案,每次 x + = p x+=p x+=p。这样的话要找 n T \frac{n}{T} Tn个答案,时间复杂度 O ( n T ) O(\frac{n}{T}) O(Tn)。
为了均摊时间复杂度,我们令
T
=
n
T
T=\frac{n}{T}
T=Tn。显然
T
=
n
T=\sqrt{n}
T=n时时间复杂度较为平均。
总时间复杂度
O
(
n
log
n
)
O(n\log n)
O(nlogn)
代码:
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=150010,M=410;
int n,m,T,x,y,a[N],cnt[M][M];
char ch;
int main()
{
scanf("%d%d",&n,&m);
T=sqrt(n);
if (T*T<n) T++;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
for (int j=1;j<=T;j++)
cnt[j][i%j]+=a[i];
}
while (m--)
{
while (ch=getchar()) if (ch=='A'||ch=='C') break;
if (ch=='A')
{
scanf("%d%d",&x,&y);
if (x<=T) printf("%d\n",cnt[x][y]);
else
{
int ans=0;
for (int i=y;i<N;i+=x)
ans+=a[i];
printf("%d\n",ans);
}
}
else
{
scanf("%d%d",&x,&y);
for (int j=1;j<=T;j++)
cnt[j][x%j]-=a[x];
a[x]=y;
for (int j=1;j<=T;j++)
cnt[j][x%j]+=a[x];
}
}
return 0;
}