[luogu3396] 哈希冲突

题意

Here

思考

很早之前做的这一题,当时觉得这题的根号平衡思想很赞,现在重新回顾一遍,记录下来。

简要题意:给你 \(x,p\) ,从 \(x\) 开始,每隔 \(p\) 个数取一个数,求和

暴力的想法是直接枚举,计算,复杂度 \(O(n^2)\),当然我们也可以对答案进行简单的预处理,令 \(ans[p][i]\) 表示模数是 \(p\),余数是 \(i\)的答案,但预处理复杂度同样是 \(O(n^2)\), 但我们实现 \(O(1)\) 询问了,期望得分 \(10\)

我们得到的两种算法,第一种不用预处理,但询问是 \(O(n^2)\) 的,第二种 \(O(n^2)\) 的预处理,但询问是 \(O(1)\) 的,我们考虑能否将这两种算法中和一下:只预处理 \(p\in[1,\sqrt n]\)\(ans[p][i]\),预处理的复杂度变为了 \(O(n\sqrt n)\),考虑询问,若单次询问的 \(p \leq\sqrt n\),那么它是 \(O(1)\) 的,若 \(p > \sqrt n\),我们暴力统计,易知少于 \(\sqrt n\) 个数对答案有贡献,那么询问的复杂度是 \(O(\sqrt n)\) 的,对于修改操作,我们只需要修改 \(ans[][]\)数组,复杂度\(O(\sqrt n)\), 总体复杂度 \(O((n+m)\sqrt n)\)

代码

#include<bits/stdc++.h>
using namespace std;
const int N = 150050;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x * f;
}
int n, m;
int ans[110][110];/// ans[p][i] 对p取模 余数为i
int val[N], MAX, t;
int query(int p, int la){
    if(p <= t) return ans[p][la];
    int ANS = 0;
    for(int i=la; i<=n; i+=p){
        ANS += val[i];
    }
    return ANS;
}
void change(int pos, int v){
    for(int j=1; j<=t; j++){
        ans[j][pos % j] = ans[j][pos % j] - val[pos] + v;
    }
    val[pos] = v;
}
int main(){
    n = read(); m = read();
    t = sqrt(n);
    for(int i=1; i<=n; i++) val[i] = read();
    for(int i=1; i<=n; i++){
        for(int j=1; j<=t; j++){
            ans[j][i % j] += val[i];
        }
    }
    while(m --){
        char a; cin >> a;
        int x, y; cin >> x >> y;
        if(a == 'A'){
            cout<<query(x, y)<<endl;
        }
        else change(x, y);
    }
    return 0;
}

总结

本题的根号平衡思想真的很好,通过对小于 \(\sqrt n\) 和大于 \(\sqrt n\) 的范围进行分治,将复杂度优化成根号

posted @ 2018-11-08 22:34  alecli  阅读(96)  评论(0编辑  收藏  举报