P3396 哈希冲突 (根号算法)

题目链接:https://www.luogu.org/problemnew/show/P3396

题目描述

众所周知,模数的hash会产生冲突。例如,如果模的数p=7,那么411便冲突了。

B君对hash冲突很感兴趣。他会给出一个正整数序列value[]

自然,B君会把这些数据存进hash池。第value[k]会被存进(k%p)这个池。这样就能造成很多冲突。

B君会给定许多个px,询问在模p时,x这个池内数的总和

另外,B君会随时更改value[k]。每次更改立即生效。

保证1<=p<n1<=p<n.

输入输出格式

输入格式:

 

第一行,两个正整数n,m,其中n代表序列长度,m代表B君的操作次数。

第一行,n个正整数,代表初始序列。

接下来m行,首先是一个字符cmd,然后是两个整数x,y

  • cmd='A',则询问在模x时,y池内数的总和。

  • cmd='C',则将value[x]修改为y

 

输出格式:

 

对于每个询问输出一个正整数,进行回答。

 

输入输出样例

输入样例#1: 
10 5
1 2 3 4 5 6 7 8 9 10
A 2 1
C 1 20
A 3 1
C 5 1
A 5 0
输出样例#1:
25
41
11

说明

样例解释

A 2 1的答案是1+3+5+7+9=25.

A 3 1的答案是20+4+7+10=41.

A 5 0的答案是1+10=11.

数据规模

对于10%的数据,有n<=1000,m<=1000.

对于60%的数据,有n<=100000.m<=100000.

对于100%的数据,有n<=150000,m<=150000.

保证所有数据合法,且1<=value[i]<=1000.


 

看了一下题,只会暴力,在分块专题里找的,疯狂找分块方案,还是不会QWQ,看大佬的题解,蒟蒻只能Orz,

这应该可以算是一个神奇的分块吧qwq,下面请看大佬的题解


 

这是一道论文题。集训队论文《根号算法——不只是分块》。

首先,题目要我们求的东西,就是下面的代码:

for(i=k;i<=n;i+=p)
    ans+=value[i];

即:从 k开始,每隔p个数取一个数,求它们的和。

这个算法的复杂度是O(n^2)O(n2)的。

令答案为ans[p][k]ans[p][k],表示模数是p,余数是k.

那么,对于第i个数,如何处理它对ans的贡献呢?

for(p=1;p<=n;p++) //枚举模数
    ans[p][i%p]+=value[i]; //处理对应的贡献

这样看上去很妙的样子,然而O(n^2)O(n2)的预处理, O(1)O(1)询问,空间复杂度还是

O(n^2)O(n2)的

所以我们很自然地想到:只处理[1,\sqrt{n}][1,n]以内的p

这样的话,令 size=\sqrt{n}size=n,则可以这样预处理:

for(p=1;p<=size;p++) //只枚举[1,size]中的
    ans[p][i%p]+=value[] //处理对应的贡献

于是预处理的复杂度降到了 O(n\sqrt{n})O(nn).

接着考虑询问。如果询问的p<size ,那显然可以O(1)O(1)给出回答。

如果p超过size,我们就暴力统计并回答。因为 p>\sqrt{n}p>n,所以少于\sqrt{n}n个数对答案有贡献。所以对于 p>\sqrt{n}p>n,暴力统计的复杂度是 O(\sqrt{n})O(n)..

接着考虑修改。显然我们把p<size的值全都更新一遍就行。复杂度也是 O(\sqrt{n})O(n).

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数组 
}

这样,我们就在O((m+n)\sqrt{n})O((m+n)n).的时间内完成了任务

自己ac的代码

/**
 *        ┏┓    ┏┓
 *        ┏┛┗━━━━━━━┛┗━━━┓
 *        ┃       ┃  
 *        ┃   ━    ┃
 *        ┃ >   < ┃
 *        ┃       ┃
 *        ┃... ⌒ ...  ┃
 *        ┃       ┃
 *        ┗━┓   ┏━┛
 *          ┃   ┃ Code is far away from bug with the animal protecting          
 *          ┃   ┃   神兽保佑,代码无bug
 *          ┃   ┃           
 *          ┃   ┃        
 *          ┃   ┃
 *          ┃   ┃           
 *          ┃   ┗━━━┓
 *          ┃       ┣┓
 *          ┃       ┏┛
 *          ┗┓┓┏━┳┓┏┛
 *           ┃┫┫ ┃┫┫
 *           ┗┻┛ ┗┻┛
 */


#include <bits/stdc++.h>
#include <iostream>

using namespace std;

#define  ll long long
const int maxn=150000+5;
const int maxn1=1500+5;
bool cmp(){
    return 0;
}
int a[maxn];
int ans[maxn1][maxn1];
//set<int> a,b;
set<pair<int,int> > ps;
set<pair<int,int> > pt;

int n,q;
void init(){
    for(int i=1; i<=n; ++i)
        for(int p=1; p*p<=n; ++p)
        ans[p][i%p]+=a[i];
}

void updata(int x, int val){
    for(int p=1; p*p<=n; ++p){
        ans[p][x%p]+=(val-a[x]);
    }
    a[x]=val;
}


int main(){
    std::ios::sync_with_stdio (false);
    int flag=1;
    int x,y;
    cin>>n>>q;
    for(int i=1; i<=n; ++i) cin>>a[i];
    init();
    while(q--){
        string cmd;
        cin>>cmd>>x>>y;
        if(cmd=="A"){
            if(x*x<=n) cout<<ans[x][y]<<"\n";
            else {
                int ret=0;
                for(int i=y; i<=n; i+=x) ret+=a[i];
                cout<<ret<<"\n";
            }
        }
        else updata(x,y);
    }
    return 0;
}
View Code

 

posted @ 2019-07-09 20:40  mrdushe  阅读(323)  评论(0编辑  收藏  举报