牛客寒假训练营第二场D-整除分块

牛客寒假训练营第二场D题

Description:

整除分块,又称数论分块。是数论算法中的重要技巧,你可以在各种需要枚举因子的连续求和类问题中见到它的身影。如杜教筛,莫比乌斯反演化简后的整除分段求和等。balabala
...(复制过来乱码,遂放弃),题目链接:https://ac.nowcoder.com/acm/contest/9982/D

题意就是(S = {lfloor dfrac{N}{i} floor}),其中(iin[1, N]),输入T,N,x。T表示测试的个数,求每个测试(lfloor dfrac{N}{x} floor)去重集合S中降序排序第几大。

示例输入

2 // 测试的个数
25 9
1000000000 1000000000

示例输出

8
63244

说明

当n=25时候,(S={25,12,8,6,5,4,3,2,1}) (lfloor dfrac{25}{9} floor=2),2在S集合中是降序排列的第8个,所以输出8

Solution:

1、首先了解一下整除分块的性质,对于一个正整数N

1、(lfloor dfrac{N}{i} floor)最多有(2sqrt{N})

证明:

  • (i le sqrt{N}), 一共有(sqrt{N})
  • (i > sqrt{N})(dfrac{N}{i} <= sqrt{N}), 最多有(sqrt{N})种;因为有可能对于所有的(i > sqrt{n})(dfrac{N}{i} < sqrt{N}),那么此时只有(sqrt{N} - 1)

根据这个题目别人的代码我可以知道:当(lfloor sqrt{N} floor = lfloor dfrac{N}{sqrt{N}} floor)时,有(2sqrt{N} - 1)种(不知道如何证明暂时

2、(b = lfloor dfrac{N}{a} floor) 时, (a = lfloor dfrac{N}{b} floor)

这个类似于实数除法中除数和商的互逆性质:$b = dfrac{N}{a} $ 时, (a = dfrac{N}{b})

不知如何证明

3、对于(lfloor dfrac{N}{j} floor)(lfloor dfrac{N}{i} floor)相等时,j最大的取值是(lfloor dfrac{N}{lfloor dfrac{N}{i} floor} floor)

也就是说(forall j in [i, lfloor dfrac{N}{lfloor dfrac{N}{i} floor} floor])(lfloor dfrac{N}{j} floor = lfloor dfrac{N}{i} floor)

证明看这个哥哥的:https://www.cnblogs.com/0xfffe/p/9648943.html

2、本题解法

#include<bits/stdc++.h>
using namespace std;
int T, N, x;
int main()
{
	scanf("%d", &T);
	while(T--){
		scanf("%d %d", &N, &x);
		int sq = sqrt(N);
		int tmp = N/x;
        int tot;
        // 求出S中集合元素个数(即有多少种数字)
        if(sq==N/sq) tot = 2*sq-1;
        else		 tot = 2*sq;
        
        // 如果 x >= sqrt(n)
		if(x>=sq) printf("%d
",tot-tmp+1);
        // else
		else printf("%d
",x); // N/x < sqrt(n)
	}
	return 0;
}

这里主要用到了性质1和2,

比如对于N=25,(S={25,12,8,6,5,4,3,2,1})

  • x = 2时,25/2 = 12,ans = 2;也就是说ans = x,这是因为x 和 anx/x 在S中是关于(sqrt{N})对称的
  • x = 10时,25/10 = 2, ans = |S| - 2+1 = 8; 即ans = |S| - N/x + 1

对于另一个类似的题目,主要用到性质3

(sum_{i=1}^{N} lfloor dfrac{N}{i} floor)

// 求和模板
#define long long ll
void summ(ll  N)
{
	ll ans;
	for(ll l=1, r; l<=n; l=r+1){
		r = n/(n/i); // 区间右端点 
		ans += (r-l+1)*(n/i); // [l,r]区间内部的都是n/i 
	}
	return ans;
 } 

参考

https://www.limstash.com/articles/201901/1206

https://www.cnblogs.com/hunxuewangzi/p/14373198.html

https://www.cnblogs.com/0xfffe/p/9648943.html

原文地址:https://www.cnblogs.com/VanHa0101/p/14374915.html