数论分块
标题数论分块是一种非常重要的思想。就是对于一些表达式,它的值只有sqrt(n)种,那么我们就对于这sqrt(n)种数值进行分块,然后暴力算即可。
简单来说就是:
把一个数列分成一块一块的区域进行计算
下面我们来详细分析一些题目和温故一些知识点:
“ ⌊ ⌋” ←这个符号表示向下取整(⌊x⌋:表示小于 x 的最大整数;⌈x⌉:表示大于 x 的最小整数。)
mod的等价公式:
k mod i = k − ⌊ k / i ⌋× i 。
引入一个经典题目:
求∑Ni=1 ⌊N/i⌋ ,N≤1012
我们发现如下性质:
- 首先我们要了解⌊N/i⌋最多只有2√N种取值
证明:对于 i≤√N , 只有 √N 种,对于 i>√N, Ni<√N,也只有 √N 种取值,共计 √N 种
数列呈现出来大概是这样的:abccdddeeeeeee
- 枚举左端点和右端点。设左端点为i ,右端点为 i′=
假设
⌊N/i′⌋ 与 ⌊N/i⌋ 相等,则 i′ 的最大值为 ⌊N/⌊N/i⌋ ⌋
证明:
这样一来思路就比较明显了
两个指针 i 和 p,i的初始值是1,每次令p=
把(p-i+1)* ⌊N/i⌋ 累加,控制更新 i = p + 1 就是答案(O(√N))
模板:
for(int i=1,p;i<=n;i=j+1){
p=n/(n/i);
ans+=(n/i)*(p-i+1);
}
那么我们来看几道题熟悉一下:
P2261 [CQOI2007]余数求和
题目描述:
给出正整数 n 和 k 计算 G(n, k)=k mod 1 + k mod 2 + k mod 3 + …+k mod n的值 其中 k mod i表示 k 除以 i 的余数。
例如 G(10, 5)=5 mod 1 + 5 mod 2 + 5 mod3 + 5 mod 4 + 5 mod 5 … + 5 mod10=0+1+2+1+0+5+5+5+5+5=29
Input
两个整数 n ,k
Output
答案
Sample Input
10 5
Sample Output
29
分析:
由题意得:
由 k mod i = k − ⌊ k / i ⌋× i 得:
样例打表如下:
因为有连续的⌊N/i⌋的值是一样的,所以我们可以用除法分块来解决问题。
求
我们设
t=⌊k/i⌋,
需要注意的是,当t 等于0时候,右边界p一定在n位置上(正数求余结果>=0)。即:
if(t==0)p=n
当t不等于0时候,为了防止他右边界越界,我们将右边界p加上一个min函数防止其大于n。
if(t!=0)p=min(n,⌊k/⌊k/i⌋⌋)
每一块的和 = 当前块的 × 当前块元素个数 × 当前块 i 的平均值
t×(p-i+1)×(p+i) /2
最后结果用n×k-⌊k/i⌋×(p-i+1)×(p+i) /2即为答案。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<math.h>
#include<cmath>
using namespace std;
typedef long long ll;
ll sum=0, n,k;
int main()
{
cin>>n>>k;
sum=n*k;
int p;
for(ll i=1,t;i<=n;i=p+1)//i,p就是一块里面的左右边界
{
t=k/i;//求出当前的t
if (t==0) p=n;
else p=min(k/t,n);
//p=(k/i?min(k/(k/i),n):n);
sum-=(k/i)*(i+p)*(p-i+1)/2;
}
cout<<sum;
}
我们再看一道简单裸题:
P1403 [AHOI2005]约数研究
根据表格
1-n的因子个数,可以看成共含有2因子的数的个数+含有3因子的数的个数……+含有n因子的数的个数,在1~n中含有“2”这个因子的数有n/2个,3有n/3个,以此类推
得到公式:
f(i)=⌊n/i⌋
套用模板求解即可(代码就不粘贴了)
.
.
.
.
.
······
ps:
参考洛谷题解和博主:https://www.cnblogs.com/five20/p/9199192.html