zoukankan      html  css  js  c++  java
  • P3396 哈希冲突

    原题链接  https://www.luogu.com.cn/problem/P3396

    题目大意

    给你一个长度为 n 的序列和 m 个操作,每次操作有两种类型:

    1. 询问下标模 x 后为 y 的所有数之和;

    2. 修改第 x 个数;

    题解

    先想想暴力怎么搞:

    对于第 1 种操作,我们可以 O(n)的枚举求和,对于第 2 种操作,我们可以 O(1)修改;

        long long ans=0;
        for(int i=y;i<=n;i+=x) ans+=a[i];  //暴力枚举加和 
        printf("%lld
    ",ans);

    总的时间复杂度为 O(nm),n2 过 15w?显然不行;

    考虑一下第一种操作为什么跑的慢。

    如果模数 x 很小时,我们上面的那层 for 循环的复杂度更接近 O(n);反之,如果模数 x 很大时,复杂度反而会小;

    这就提供了我们一种思路:

    只有当 x 比较大的时候我们才暴力,如果 x 很小,我们直接记录答案!

    那么 x 什么时候才算大呢?

    一般我们钦定 x > √n 的时候我们就暴力;

    这种算法有个响当当的名字: 

    根号算法

    根号算法是一种很常见的算法;
    常见的根号思想有:双向搜索,根号分类讨论,根号重建,复杂度平衡,以及一些根号级别的数据结构如分块和莫队;
    这些算法一般是多种暴力算法的结合,一般具有较低的思维难度和编码难度

    ——ImmortalCO猫

    有的时候,我们可以对一个题想出两个暴力,各有各自的长处和短处。

    如果我们能对数据范围进行分块处理,或者两个暴力分别算之后拼接在一起,就用两个合在一起的暴力,实现了正解

    通常这个分界点可以取到 n−−√n

    所以叫根号算法。

    通过根号算法,我们就能实现两种操作时间复杂度的均分了 。

    我们用 dp [ i ][ j ] 记录模 i 为 j 的所有下标所对应的数的和;(这里数组只需开到 √n)

    O(n√n)预处理出答案:

        for(int i=1;i<=n;i++)  //枚举每个数 
            for(int j=1;j<=sqrt(n);j++)  //枚举模几
                dp[j][i%j]+=a[i]; 

    对于第 1 种操作,如果所给的模数 x < √n,那么我们直接输出答案;否则我们暴力枚举;

    时间复杂度 O(√n)

            if(C=='A')   //求模x为y的和
            {
                if(x<=sqrt(n)) printf("%lld
    ",dp[x][y]);  //直接输出 
                else                
                {
                    long long ans=0;
                    for(int i=y;i<=n;i+=x) ans+=a[i];  //暴力枚举加和 
                    printf("%lld
    ",ans);
                }
            } 

    对于第 2 种操作,我们在将第 x 个数更新的同时,也要把 dp 数组更新一遍;

    时间复杂度 O(√n)

            for(int i=1;i<=sqrt(n);i++)  //更新第x个数所涉及到的所有的池
                dp[i][x%i]+=y-a[x]; 
            a[x]=y;

    总体时间复杂度 O(n√n),这样我们就用几乎暴力的算法过掉了本题qwq

    Code:

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    using namespace std;
    int read()
    {
        char ch=getchar();
        int a=0,x=1;
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') x=-x;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            a=(a<<1)+(a<<3)+(ch-'0');
            ch=getchar();
        }
        return a*x;
    }
    const int N=150005;
    int a[N];
    long long dp[400][400];  //dp[i][j]:模i为j的所有下标所对应的数的和,只需开到√n 
    int n,m,x,y;
    char C;
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=n;i++)  //枚举每个数 
            for(int j=1;j<=sqrt(n);j++)  //枚举模几
                dp[j][i%j]+=a[i]; 
        while(m--)
        {
            cin>>C;
            x=read();y=read();
            if(C=='A')   //求模x为y的和
            {
                if(x<=sqrt(n)) printf("%lld
    ",dp[x][y]);  //直接输出 
                else     //若x>√n,直接暴力!              
                {
                    long long ans=0;
                    for(int i=y;i<=n;i+=x) ans+=a[i];  //暴力枚举加和 
                    printf("%lld
    ",ans);
                }
            } 
            else       //把第x个数改成y 
            {
                for(int i=1;i<=sqrt(n);i++)  //更新第x个数所涉及到的所有的池
                    dp[i][x%i]+=y-a[x]; 
                a[x]=y;
            }
        } 
        return 0;
    }
  • 相关阅读:
    dotnet 控制台读写 Sqlite 提示 no such table 找不到文件
    dotnet 控制台读写 Sqlite 提示 no such table 找不到文件
    dotnet 控制台 Hangfire 后台定时任务
    dotnet 控制台 Hangfire 后台定时任务
    dotnet 获取指定进程的输入命令行
    dotnet 获取指定进程的输入命令行
    PHP sqrt() 函数
    PHP sinh() 函数
    PHP sin() 函数
    PHP round() 函数
  • 原文地址:https://www.cnblogs.com/xcg123/p/12114132.html
Copyright © 2011-2022 走看看