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;
    }
  • 相关阅读:
    golang 常见疑惑总结
    golang 详解defer
    golang调试工具Delve
    ACE的源码划分
    通过#define连接字符串的特殊方法[转]
    转:extern "C"的用法解析
    转:3d max 2013 安装教程,凭着一种互联网精神提供给广大朋友
    转:Bullet物理引擎不完全指南(Bullet Physics Engine not complete Guide)
    转:折腾一晚上Bullet及Ogre相关工具的成果 -- 3Ds Max,Maya, blender, GameKit
    转:CMake快速入门教程-实战
  • 原文地址:https://www.cnblogs.com/xcg123/p/12114132.html
Copyright © 2011-2022 走看看