zoukankan      html  css  js  c++  java
  • 【洛谷3396】哈希冲突(大力分块)

    点此看题面

    大致题意: 给你一个长度为(n)的数组(val)以及(m)个操作,操作有两种:一种是将(val_x)修改为(y),另一种操作是求出(sum val_i(i\%x=y))

    朴素的暴力

    我们先来写一个朴素的暴力,代码如下:

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define abs(x) ((x)<0?-(x):(x))
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define Fsize 100000
    #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
    #define N 150000
    int FoutSize=0,OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,Fout[Fsize],OutputStack[Fsize];
    using namespace std;
    int n,Q,a[N+5];
    inline void read(int &x)
    {
        x=0;static char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    inline void read_alpha(char &x)
    {
        while(!isalpha(x=tc()));
    } 
    inline void write(int x)
    {
        if(!x) return (void)pc('0');
        while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
        while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
    }
    int main()
    {
        register int i,x,y,ans;register char op;
        for(read(n),read(Q),i=1;i<=n;++i) read(a[i]);
        while(Q--)
        {
            read_alpha(op),read(x),read(y);
            if(op^'A') a[x]=y;//对于修改操作直接O(1)修改
            else
            {
                for(ans=0,i=y;i<=n;i+=x) ans+=a[i];//据题意统计答案
                write(ans),pc('
    ');//输出
            }
        }
        return fwrite(Fout,1,FoutSize,stdout),0;
    }
    

    我们可以惊喜地发现,这样就有(91)分了。如果你写得足够优秀,说不定可以直接AC。

    预处理答案+暴力更新

    我们可以用(ans_{i,j})来记录询问(i,j)时的答案。

    首先,(O(n^2))预处理出答案,并用(O(n^2))大小的数组把答案存下来,对于询问,可以(O(1))输出答案,并(O(n))暴力更新。

    然后,我们悲剧地发现(n≤150000),还没等到(TLE),直接内存炸飞了。。。

    代码略。

    如何优化

    我们可以对第二个上天了的代码进行优化。

    记录全部答案毕竟所耗内存太大了,能不能只记录一部分的答案,然后另一部分暴力算呢?

    就可以考虑分块

    我们可以先(O(nsqrt n))暴力预处理模数(≤sqrt n)时的答案,并用(ans)数组将其存储下来。

    然后,对于修改,我们可以在(O(sqrt n))的时间复杂度内枚举每一个小于等于(sqrt n)的模数,然后更新对应的(ans)

    对于询问,我们分两种情况讨论:

    • 如果模数(≤O(sqrt n)),就直接输出(ans)
    • 如果模数(>O(sqrt n)),就暴力求解,且枚举的数的个数肯定小于(O(sqrt n))

    这样的算法应该是(O((n+m)sqrt n))的。

    一个玄学的小优化

    虽然我个人认为最优块的大小应该是(O(sqrt n)),但是,实践证明,以(O(sqrt[3] n))为块的大小要比(O(sqrt n))快很多。

    代码

    #include<bits/stdc++.h>
    #define max(x,y) ((x)>(y)?(x):(y))
    #define min(x,y) ((x)<(y)?(x):(y))
    #define abs(x) ((x)<0?-(x):(x))
    #define LL long long
    #define ull unsigned long long
    #define swap(x,y) (x^=y,y^=x,x^=y)
    #define Fsize 100000
    #define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
    #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
    #define N 150000
    #define SIZE 400 
    int FoutSize=0,OutputTop=0;char Fin[Fsize],*FinNow=Fin,*FinEnd=Fin,Fout[Fsize],OutputStack[Fsize];
    using namespace std;
    int n,Q,blo,a[N+5],ans[SIZE][SIZE];
    inline void read(int &x)
    {
        x=0;static char ch;
        while(!isdigit(ch=tc()));
        while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
    }
    inline void read_alpha(char &x)
    {
        while(!isalpha(x=tc()));
    } 
    inline void write(int x)
    {
        if(!x) return (void)pc('0');
        while(x) OutputStack[++OutputTop]=x%10+48,x/=10;
        while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;
    }
    int main()
    {
        register int i,j,x,y,res;register char op;
        for(read(n),read(Q),blo=pow(n,0.33333),i=1;i<=n;++i) read(a[i]);//读入数据,这里的块的大小取n^(1/3)要比n^(1/2)快很多
        for(i=1;i<=n;++i) for(j=1;j<=blo;++j) ans[j][i%j]+=a[i];//O(n^(4/3))暴力预处理出模数≤n^(1/3)时的答案
        while(Q--)
        {
            read_alpha(op),read(x),read(y);
            if(op^'A')//对于修改操作
            {
                for(i=1;i<=blo;++i)//枚举每一个小于等于n^(1/3)的模数,修改对应答案
                    ans[i][x%i]+=y-a[x];//减去原来的值,加上更新之后的值
                a[x]=y;//修改
            }
            else//对于询问操作
            {
                if(x<=blo) {write(ans[x][y]),pc('
    ');continue;}//如果模数≤n^(1/3),直接输出预处理得到的答案
                for(i=y,res=0;i<=n;i+=x) res+=a[i];//否则,暴力统计答案
                write(res),pc('
    ');//输出答案
            }
        }
        return fwrite(Fout,1,FoutSize,stdout),0;
    }
    
  • 相关阅读:
    springIOC 原理
    jeesite异步分页
    yum
    乐观锁原理
    equlas(),hashcode(),hashset,hashmap
    链接收藏
    java单词
    jeesite优化
    SailingEase .NET Resources Tool (.NET 多语言资源编辑器)转
    C#基本语法
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3396.html
Copyright © 2011-2022 走看看