• 浅谈根号分治


    根号分治

    根号分治是一种优美的暴力。

    顾名思义,根号分治就是对于一个长度为 N N N的数列,将查询和修改分为 ≤ N \leq N N > N >N >N的两个部分来处理。将两个部分分别处理并拼在一起,来优化时间复杂度。


    例题

    洛谷P3396 哈希冲突

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

    • 查询所有满足 k % x = y k\% x=y k%x=y a k a_k ak之和
    • a x a_x ax修改为 y y y

    1 ≤ n ≤ 150000 , 1 ≤ m ≤ 150000 , 1 ≤ a i ≤ 1000 1\leq n\leq 150000,1\leq m\leq 150000,1\leq a_i\leq 1000 1n150000,1m150000,1ai1000

    解题思路

    首先,我们考虑暴力。

    code

    #include
    using namespace std;
    int n,m,B,x,y,ans,a[150005];
    char ch;
    int main()
    {
    	scanf("%d%d",&n,&m);
    	B=sqrt(n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	while(m--){
    		ch=getchar();
    		while(ch!='A'&&ch!='C') ch=getchar();
    		scanf("%d%d",&x,&y);
    		if(ch=='A'){
    			ans=0;
    			for(int i=y;i<=n;i+=x) ans+=a[i];
    			printf("%d\n",ans);
    		}
    		else a[x]=y;
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这个暴力是 O ( n m ) O(nm) O(nm)的,我们考虑优化。

    我们发现,如果当前是第一种操作且 x > n x>\sqrt n x>n 时,一次查询是 O ( n ) O(\sqrt n) O(n )的。然而,在 x ≤ n x\leq \sqrt n xn 时,时间复杂度就变大了。我们考虑对 x ≤ n x\leq \sqrt n xn 的部分特殊处理一下。

    f i , j f_{i,j} fi,j表示模数为 i i i时所有下标模 i i i j j j的数之和,那么我们用 O ( n n ) O(n\sqrt n) O(nn )处理出 1 ≤ i ≤ s q r t n 1\leq i\leq sqrt n 1isqrtn 0 ≤ j < i 0\leq j0j<i的所有 f i , j f_{i,j} fi,j之和。

    那么,在查询时,若 x ≤ n x\leq \sqrt n xn ,直接在 f i , j f_{i,j} fi,j O ( 1 ) O(1) O(1)查询;若 x > n x>n x>n,则 O ( n ) O(\sqrt n) O(n )暴力查询。在修改时,我们 O ( n ) O(\sqrt n) O(n )修改 f i , j f_{i,j} fi,j,再 O ( 1 ) O(1) O(1)修改 a i a_i ai,那么时间复杂度就降为 O ( ( n + m ) n ) O((n+m)\sqrt n) O((n+m)n )

    code

    #include
    using namespace std;
    int n,m,B,x,y,ans,a[150005],f[405][405];
    char ch;
    int main()
    {
    	scanf("%d%d",&n,&m);
    	B=sqrt(n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    	}
    	for(int i=1;i<=B;i++){
    		for(int j=1;j<=n;j++) f[i][j%i]+=a[j];
    	}
    	while(m--){
    		ch=getchar();
    		while(ch!='A'&&ch!='C') ch=getchar();
    		scanf("%d%d",&x,&y);
    		if(ch=='A'){
    			if(x<=B) printf("%d\n",f[x][y]);
    			else{
    				ans=0;
    				for(int i=y;i<=n;i+=x) ans+=a[i];
    				printf("%d\n",ans);
    			}
    		}
    		else{
    			for(int i=1;i<=B;i++) f[i][x%i]+=y-a[x];
    			a[x]=y;
    		}
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    Python基础(一)基本类型
    elasticsearch DSL部分 源码解析
    SoftwareTest6 - 用 Selenium 怎么点点点
    解决oss视频上传后截取的第一帧图片被旋转问题
    docker删除镜像、容器命令
    PDAC复盘法是什么?怎么用?
    lotus MaxStorage 限制存储空间使用
    Java多线程(5)----浅谈读写锁
    Redis系列——5种常见数据类型day1-3
    C++ —— 引用
  • 原文地址:https://blog.csdn.net/tanjunming2020/article/details/133779812