数论分块

质数判定

单个素数判定
bool is_prime(ll x)
{
    for(int i=2;i*i<=x;++i)
        if(x%i==0)return false;
    return true;
}
多个素数预处理
const int maxn = 1e6 + 10;
bool vis[maxn];
int pri[maxn/5],tot;
void init()
{
    for(int i=2;i<=n;i++){
        if(!vis[i])pri[++tot]=i;
        for(int j=1;i*pri[j]<=n&&j<=tot;j++){
            vis[i*pri[j]]=1;
            if(i%pri[j]==0)break;
            //欧拉筛的精髓就在于break;
        }
    }
}

为什么需要break呢,或者说为什么这个时间段需要break.

解决这个问题之前需要知道,欧拉筛是利用什么来加速筛选的。(每个合数都会被它的最小质因子筛去)

i % pri[j] ==0 ,则 i存在因子pri[j],又pri[j]递增,因此后面所有的pri[j]将不能作为最小质因子筛去i*pri[j]

题外话

很多题目要求判断1e9内的相邻素数,不能使用素数筛法

经测试,1e9内相邻素数的最大距离是282,暴力判断就行,通过这一点衍生出很多结论,莽就完事了

数论分块

很多时候可以见到这类的式子
∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^n{\lfloor \frac{n}{i} \rfloor} i=1nin
朴素的想法是for循环,然而只能处理2e7.

我们试着打一下表格

123456789101112131415161718
11
221
3311
44211
552111
6632111
77321111
884221111
9943211111
1010532211111
11115322111111
121264322111111
1313643221111111
14147432221111111
151575332211111111
1616854322211111111
17178543222111111111
181896433222111111111

发现对于特定的N存在连续 的i使得 ⌊ N i ⌋ \lfloor \frac{N}{i} \rfloor iN值相等

如果我们可以知道连续的i的起点和终点,那么就可以通过(r-l+1)*(N/l)来得到区间的答案

我们不知道谁是起点,那么有没有可能对于任意的起点得到其终点呢。

对于任意的Ni都有
N =   C + λ ∗ i N = \ C + {\lambda*i} N= C+λi
此时
λ = ⌊ N i ⌋ \lambda = \lfloor \frac{N}{i} \rfloor λ=iN
对于第一个式子,特定的 N固定i的取值后会得到最大的 “ λ \lambda λ”。同样的,若固定此时的“ λ \lambda λ”便能得到最大的i

也就是说已知i R = N / λ R = N/ \lambda R=N/λ.同时 λ = ⌊ N i ⌋ \lambda = \lfloor \frac{N}{i} \rfloor λ=iN.即 R = N / ( N / i ) R= N/(N/i) R=N/(N/i).

至此,我们已经知道了对于任意的起点获取其终点的方式.同时不难发现 新的起点就是上一个终点+1.那么代码就已经呼之欲出了

ll solve(ll n)
{
    ll ans=0;
    for(ll l=1,r;l<=n;l=r+1){
        r = n/(n/l);
        ans += (r-l+1)*(n/l);
    }
    return ans;
}

好了现在你已经学会数论分块了.那么来做个练习题吧

BZOJ 1257
题面是 ∑ i N   N   m o d   i \sum_i^N{\ N \space mod \space i} iN N mod i

∑ i N   N   m o d   i = ∑ i N N − ⌊ N i ⌋ ∗ i \sum_i^N{\ N \space mod \space i} = \sum_i^N{N- \lfloor \frac{N}{i} \rfloor * i} iN N mod i=iNNiNi

即便不会也没有关系,继续往下学.

多关键字数论分块

这个样子
∑ i m i n ( N , M ) ⌊ N i ⌋ ⌊ M i ⌋ \sum_i^{min(N,M)}{\lfloor\frac{N}{i}\rfloor\lfloor\frac{M}{i}\rfloor} imin(N,M)iNiM
现在对于N,i而言L~R1区间的值都是 ⌊ N i ⌋ \lfloor \frac{N}{i} \rfloor iN,对于M,i而言L~R2区间的值都是 ⌊ M i ⌋ \lfloor \frac{M}{i} \rfloor iM,我们只需控制   C = ⌊ N i ⌋ ⌊ M i ⌋ \ C= {\lfloor\frac{N}{i}\rfloor\lfloor\frac{M}{i}\rfloor}  C=iNiM .

C不变时,那么求和范围就是两个区间的交集,也就是L~min(R1,R2).

那么答案就是(min(R1,R2) - l +1)*(N/l)*(M/l)

ll solve(int n,int m)
{
    ll ans=0;
    if(n>m)swap(n,m);
    for(int l=1,r;l<=n;l=r+1){
        r = min(n/(n/l),m/(m/l));
        ans += (r-l+1)*(n/l)*(m/l);
    }
}

数论分块应用

f ( x ) = 1 f(x) = 1 f(x)=1,则
∑ i n ⌊ N i ⌋ = ∑ i n ⌊ N i ⌋ ∗ f ( i ) \sum_i^n\lfloor \frac{N}{i} \rfloor = \sum_i^n{\lfloor\frac{N}{i}\rfloor*f(i)} iniN=iniNf(i)
如果我们先对 f ( x ) f(x) f(x)函数预处理好前缀和,那么原答案的求法将会是这样子.

在这个求和计算中,可以将 ⌊ N i ⌋ \lfloor \frac{N}{i} \rfloor iN理解为一个系数,而相当一段长度的1是同一个系数.
c = ⌊ N i ⌋   i ∈ [ L , R ] ⌊ N L ⌋ f ( L ) + ⌊ N L + 1 ⌋ f ( L + 1 ) + ⌊ N L + 2 ⌋ f ( L + 2 ) + . . . + ⌊ N R ⌋ f ( R ) → c ∗ ( f ( L ) + f ( L + 1 ) + f ( L + 2 ) + . . . + f ( R ) ) = c ∗ ( s u m [ R ] − s u m [ L − 1 ] ) c = \lfloor \frac{N}{i} \rfloor \space i\in [L,R] \\ \lfloor \frac{N}{L} \rfloor f(L) + \lfloor \frac{N}{L+1} \rfloor f(L+1) +\lfloor \frac{N}{L+2} \rfloor f(L+2)+...+ \lfloor \frac{N}{R} \rfloor f(R) \\ \rightarrow c*(f(L)+f(L+1)+f(L+2)+...+f(R)) = c*(sum[R]-sum[L-1]) c=iN i[L,R]LNf(L)+L+1Nf(L+1)+L+2Nf(L+2)+...+RNf(R)c(f(L)+f(L+1)+f(L+2)+...+f(R))=c(sum[R]sum[L1])

const int maxn = 1e6+10;
int f[maxn];
void init()
{
    for(int i=1;i<=n;i++){
        f[i]=1;
        f[i]+=f[i-1];
    }
}

ll solve(ll n)
{
    init();
    ll ans=0;
    for(ll l=1,r;l<=n;l=r+1){
        r = n/(n/l);
        ans += (f[r]-f[l-1])*(n/l);
    }
    return ans;
}

若给出 f ( x ) = f ( x − 1 ) + f ( x − 2 ) f(x)=f(x-1)+f(x-2) f(x)=f(x1)+f(x2)呢?显然,需要更改的只是init()函数其他的照写就行

因此我们发现,对于给定的任意式子形如 ∑ i n ⌊ N i ⌋ ∗ f ( i ) \sum_i^n\lfloor \frac{N}{i}\rfloor*f(i) iniNf(i) 的式子只需要求出 f ( i ) f(i) f(i)后作前缀和后数论分块求解即可

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值