洛谷P3396哈希冲突

题意:
给出长度为n的序列A1、A2、A3……An,进行M次操作:‘A’操作输入x、y,求序列中下标i%x==y的所有数的和;‘C’操作输入x、y,将Ax的值改为y。

题解:

题解真的非常神奇……思路是这样的:

首先暴力的想法:

for(int i=y;i<=n;i+=x) ans+=A[i];
纯暴力

这样是O(n^2)的。之后有O(1)求答案的预处理,预处理出每个数在下标%p时对答案的贡献:

void Init(){
    for(int i=1;i<=n;i++){
        for(int p=1;p<n;p++){
            ans[p][i%p]+=a[i];
        }
    }
    return;
}
预处理

然而,这个预处理的时间和空间复杂度都达到了O(n^2)……不过我们可以利用根号来进行优化:

void Init(){
    int size=sqrt(n);
    for(int i=1;i<=n;i++){
        for(int p=1;p<=size;p++){
            ans[p][i%p]+=a[i];
        }
    }
    return;
}
根号预处理

这样时间和空间的复杂度降到了O(n*sqrt(n))。

预处理后,对于询问中模数小于等于sqrt(n)的,我们都可以O(1)解决;而对于我们没有处理出的,即模数大于sqrt(n)的,我们知道序列里对答案有贡献的数不会超过sqrt(n)个,所以暴力求和,时间复杂度维持在O(sqrt(n))以下。

对于修改,暴力更新预处理的结果即可,复杂度维持在O(sqrt(n))以下。

所以总复杂度为O(n*sqrt(n))。

只能说根号还是很神奇的……

代码:

#include<cstdio>
#include<cmath>
#define MXN 150000+1
#define MXM 150000+1
int n,m;
int a[MXN],ans[400][400];
char cmd[2];
int x,y,size;
void Init(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    size=sqrt(n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=size;j++){
            ans[j][i%j]+=a[i];
        }
    }
    return;
}
void Query(){
    scanf("%d%d",&x,&y);
    if(x<=size) printf("%d\n",ans[x][y]);
    else{
        int sum=0;
        for(int i=y;i<=n;i+=x) sum+=a[i];
        printf("%d\n",sum);
    }
    return;
}
void Change(){
    scanf("%d%d",&x,&y);
    for(int i=1;i<=size;i++){
        ans[i][x%i]=ans[i][x%i]-a[x]+y;
    }
    a[x]=y;
    return;
}
int main(){
    Init();
    while(m--){
        scanf("%s",cmd);
        if(cmd[0]=='A') Query();
        if(cmd[0]=='C') Change();
    }
    return 0;
}
代码

 

posted @ 2018-03-22 19:45  Halifuda  阅读(194)  评论(0编辑  收藏  举报