官方题解:这是一道论文题。集训队论文《根号算法——不只是分块》。
首先,题目要我们求的东西,就是下面的代码:
for(i=k;i<=n;i+=p)
ans+=value[i];
即:从 k开始,每隔p个数取一个数,求它们的和。
这个算法的复杂度是的。
令答案为,表示模数是p,余数是k.
那么,对于第i个数,如何处理它对ans的贡献呢?
for(p=1;p<=n;p++) //枚举模数
ans[p][i%p]+=value[i]; //处理对应的贡献
这样看上去很妙的样子,然而的预处理, 询问,空间复杂度还是
的
所以我们很自然地想到:只处理以内的p
这样的话,令 ,则可以这样预处理:
for(p=1;p<=size;p++) //只枚举[1,size]中的
ans[p][i%p]+=value[] //处理对应的贡献
于是预处理的复杂度降到了 .
接着考虑询问。如果询问的p<size ,那显然可以给出回答。
如果p超过size,我们就暴力统计并回答。因为 ,所以少于个数对答案有贡献。所以对于 ,暴力统计的复杂度是 ..
接着考虑修改。显然我们把p<size的值全都更新一遍就行。复杂度也是 .
void change(int i,int v) //将value[i]改为v
{
for(p=1;p<=size;p++)
ans[p][i%p]=ans[p][i%p]-value[i]+v; //更新答案
value[i]=v; //更新value数组
}
这样,我们就在.的时间内完成了任务
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #include <cmath> 6 using namespace std; 7 const int Maxn=150100; 8 const int Sqrt=2000; 9 int n,m,Block,a[Sqrt][Sqrt],x,y,v[Maxn]; 10 int main() 11 { 12 scanf("%d%d",&n,&m); Block=(int)sqrt(n); 13 for (int i=1;i<=n;i++) scanf("%d",&v[i]); 14 memset(a,0,sizeof(a)); 15 for (int i=1;i<=Block;i++) 16 for (int j=1;j<=n;j++) a[i][j%i]+=v[j]; 17 for (int i=1;i<=m;i++) 18 { 19 char ch=getchar(); 20 while (ch!='A' && ch!='C') ch=getchar(); 21 scanf("%d%d",&x,&y); 22 if (ch=='A') 23 { 24 if (x<=Block) printf("%d\n",a[x][y]); else 25 { 26 int Ret=0; for (int i=y;i<=n;i+=x) Ret+=v[i]; printf("%d\n",Ret); 27 } 28 } 29 if (ch=='C') 30 { 31 for (int i=1;i<=Block;i++) 32 a[i][x%i]=a[i][x%i]-v[x]+y; 33 v[x]=y; 34 } 35 } 36 return 0; 37 }
其实这道题因为数据弱暴力都能过