数论-整除分块
【题意】
给出正整数n和k,计算j(n, k)=k mod 1 + k mod 2 + k mod 3 + … + k mod n的值。
例如j(5, 3)=3 mod 1 + 3 mod 2 + 3 mod 3 + 3 mod 4 + 3 mod 5=0+1+0+3+3=7。
【输入格式】
输入仅一行,包含两个整数n, k。
【输出格式】
输出仅一行,即j(n, k)。
【数据范围】
1≤n,k≤10^9
【输入样例】
5 3
【输出样例】
7
整除分块:
此题目中,k mod i等效于k-floor(k/i)*i;(floor代表向下取整)。所以这个题可以化为求k*n-floor(k/i)*i;就是求floor(k/i)*i的和。
可惜的时,数据量太大,O(n)不都可以,会发现,有一些floor(k/i)是相同的。
例如17/7==17/6=2。接下来判断相同的区间即可。对于一个商floor后例如2,分母最大可以到floor(k/2)。如果是k/2+1,那么k/(k/2+1)=2*k/(k+2)=2-(4/(k+2))<2,floor后是1。
当然,这一切讨论分母都是从1递增谈起。
核心代码:(记录商为floor(k/l)的个数)
ll l = 1,r;
for(l = 1;l <= n;l = r+1){
if(k/l == 0) break;
r = min(k/(k/l),n);
num[k/l]= *(r-l+1);
}
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<map>
#include<cmath>
#include<vector>
#include<cstdio>
using namespace std;
typedef long long ll;
ll n,k;
int main(){
cin >> n >> k;
ll ans = n*k;
ll l = 1,r;
for(l = 1;l <= n;l = r+1){
if(k/l == 0) break;
r = min(k/(k/l),n);
ans -= ((k/l)*(l+r)*(r-l+1)/2);
}
cout << ans << endl;
return 0;
}
求1-n中所有的因子个数。
N <=1e6;
暴力显然不行,寻求O(n)复杂度的思路。
有容斥原理的一些思想知道,对于一个n,那么是i的倍数的个数有n/i,也就是这n/i个数中都含有i因子。
可以这样想,把n从头开始,每隔i就截断(最后不足i的话就舍掉,毕竟会floor掉)。这样分成了几段,每一个段的末尾是这个段唯一一个可以整除i的点,也就是含有因子i的元素。
代码:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main(){
ll n;
cin >> n;
ll ans = 0;
for(int i = 1;i <= n;i++)
ans += (n/i);
cout << ans << endl;
return 0;
}