D.牛牛与整除分块
题目链接:https://ac.nowcoder.com/acm/contest/9982/D
题目描述:
整除分块,又称数论分块。是数论算法中的重要技巧,你可以在各种需要枚举因子的连续求和类问题中见到它的身影。如杜教筛,莫比乌斯反演化简后的整除分段求和等。
整除分块基于这样一个数学现象:对于任意正整数N,集合的大小总是严格小于2 √N,例如当N=10时S={10,5,3,2,1},这就使得对于
类型的求和类问题,只要快速枚举S集合,就能在√N 级别的复杂度内解决问题。
⌊ ⌋符号是向下取整符,⌊x⌋表示不大于x的最大正整数。
牛牛在学习整除分块这一算法后提出了一个新的问题,对于给定正整数N,x,令时在S中是第几大呢(去重降序排序后第几个)?
输入描述:
第一行输入一个正整数T(1 ≤ T ≤ 10^6 ),表示测试案例的数目,对于每组案例。
一行两个正整数N,x(1 ≤ x ≤ N ≤ 10^9 )。
输出描述:
对于每个案例,输出一个正整数,即在集合S中降序排第几大。
示例1:
输入
2
25 9
1000000000 1000000000
输出
8
63244
说明
当n=25时候,S={25,12,8,6,5,4,3,2,1}⌊ 25/9⌋=2,2在S集合中是降序排列的第8个,所以输出8。
解题思路:
找规律
当k小的时候答案为k,通过找规律可以发现划分的界限大概为√N附近,以25为例,25/i的值为25,12,8,6,5,4,3,2,1。将最大数与最小数两两配对,发现它们的乘积最接近25。因此当x≤√N时,每个x对应不同的答案。否则看N/√N与⌊ N/x⌋之间差多少,根据对称性即可得出结果。
代码如下:
#include <iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
using namespace std;
typedef long long ll;
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
ll x,n;
scanf("%lld %lld", &n,&x);
ll cnt = floor(sqrt(1.0*n));
ll k = n/(cnt+1);
if(x <= cnt)
printf("%lld\n",x);
else
printf("%lld\n",cnt+(k-n/x)+1);
}
return 0;
}