Dividing(整除分块)
今天打了牛客多校的比赛,H题一开始在找规律,兴奋的发现了一些小规律然后暴力算一遍。
结果如下:
然后想了想直接除以2的那些其实值都是一样的,想着直接缩短了一半的时间应该能过,然后还是T。
后来想着每个值一样的区间都用乘法去做说不定就可以了(事实证明这也许就是整除分块吧,可惜之前没有学过,然后在确定区间l,r上出了问题
裂开~~
赛后问学姐,学姐说是整除分块,恍然大悟,我们当时思想和整除分块基本差不多了可惜细节不行。然后翻看了大佬的博客终于懂了一点,打算写下来有助于记忆。
题目
链接:https://ac.nowcoder.com/acm/contest/5672/H
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
The following rules define a kind of integer tuple - the Legend Tuple:
(1, k) is always a Legend Tuple, where k is an integer. if (n, k) is a Legend Tuple, (n + k, k) is also a Legend Tuple. if (n, k) is a Legend Tuple, (nk, k) is also a Legend Tuple.
We want to know the number of the Legend Tuples (n, k) where 1≤n≤N,1≤k≤K1 \le n \le N, 1 \le k \le K1≤n≤N,1≤k≤K.
In order to avoid calculations of huge integers, report the answer modulo 109+710^9+7109+7 instead.
输入描述:
The input contains two integers N and K, 1≤N,K≤10121 \le N, K \le 10^{12}1≤N,K≤1012.
输出描述:
Output the answer modulo 109+710^9+7109+7.
示例1
输入
3 3
输出
8
示例2
输入
3 9
输出
14
把问题转化成整除分块的过程暂时不是很想打了(懒),等等回寝室慢慢磨吧,现在就直接上代码吧。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD=1e9+7;
int main(){
ll n,k;
scanf("%lld%lld",&n,&k);
ll ans=n;
ll up=min(n,k); //在k>n的时候 ,有(n,k)=(n,n)+k-n
for (ll l = 2, r=1; l <=up; l = r + 1) //l和r为区间的左右端点,这区间中的所有数的值相同
{
r=min(n/(n/l),up); //求区间的方法,l的更新在for循环一行中
ans += ((r-l+1)%MOD)*(((n/l)*2)%MOD)%MOD+(r-l+1-(n%r==0))%MOD;//r-l+1为区间长度 ,(n/l*2)为值的一部分,加上后面的r-l+1 为全部,n%r==0用来判断刚好为因数的情况
ans%=MOD;
}
if(n<k)ans+=k-n; //在k>n的时候 ,有(n,k)=(n,n)+k-n ,所以在这里加上
ans%=MOD;
printf("%lld\n",ans);
}
这板子妙在哪里呢,妙就妙在 用r=min(n/(n/l),up)来求区间的右端点,然后左端点的更新放在每次循环结束。
r=min(n/(n/l),up)这个式子的证明暂时没有很弄懂,但是至少学到了,这里贴个大佬链接来看看证明过程
https://www.cnblogs.com/ZJNU-huyh/p/13373770.html
再贴个整除分块的板子
for (int l = 1, r; l <= n; l = r + 1)
{
r = n / (n / l);
ans += (r - l + 1) * (n / l);
}
如此如此,这般这般,整除分块真是妙不可言。
2020.08.01
20:42