整除分块

例题1

  ∑ 1 n n i   , n < = 1 0 12 . \ \sum_1^n \frac{n}{i}\,,n<=10^{12}.  1ninn<=1012.

题目链接.
数据范围小跑o(n)当然没问题,数据范围这么大就要另想办法。
打表观察下,设n=20
1 2 3 4 5 6 7 8 9 10 11 12 13…20
20 10 6 5 4 3 2 2 2 2 1 1 1 1
我们发现n/i的值有某些段是重复的数字。
n i = k \frac{n}{i}=k in=k,
n = k ∗ i + d , 0 < = d < i n=k*i+d, 0<=d<i n=ki+d,0<=d<i
那么 n i + d = k \frac{n}{i+d}=k i+dn=k
i + d = n k = n n i i+d=\frac{n}{k}=\frac{n}{\frac{n}{i}} i+d=kn=inn
那么i和i+d的取值就是相同的。我们找出这一段即可。
也就是我们要找出最接近n的 i ∗ k = n i*k=n ik=n,假设i<k,则i有 n \sqrt n n 个,i和k共有 2 ∗ n 2*\sqrt n 2n 个。
我们设一个段的左端点为 l l l,则右端点 r = n n l r=\frac{n}{\frac{n}{l}} r=lnn.这一段的取值都为 n l \frac{n}{l} ln,因此总值为: ( r − l + 1 ) ∗ n l (r-l+1)*\frac{n}{l} (rl+1)ln
参考代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,ans;
int main() {
	cin>>n;
	for(ll l=1,r; l<=n; l=r+1) {
		r=n/(n/l);
		ans+=(r-l+1)*(n/l);
	}
	cout<<ans<<endl;
}

例题2

P2261 [CQOI2007]余数求和$

  ∑ 1 n n % i   , n < = 1 0 12 . \ \sum_1^n n \%i\,,n<=10^{12}.  1nn%in<=1012.
n % i = n − n i ∗ i n\%i=n-\frac n i*i n%i=nini
  ∑ 1 n n % i = ∑ 1 n ( n − n i ∗ i ) \ \sum_1^n n \%i=\sum_1^n(n-\frac n i*i)  1nn%i=1n(nini)
= n ∗ n − ∑ 1 n ( n i ∗ i ) =n*n-\sum_1^n(\frac n i*i) =nn1n(ini)
需要求: ∑ 1 n ( n i ∗ i ) \sum_1^n(\frac n i*i) 1n(ini)
分段后,对每段的n/i是定值k,则这段就是ki,k(i+1)…
每段的总值为:
( r − l + 1 ) ∗ n l ∗ l + r 2 (r-l+1)*\frac{n}{l}*\frac{l+r}2 (rl+1)ln2l+r
代码也放上吧。

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long ll;

int main() {
    ll n,k;
    scanf("%lld%lld",&n,&k);//30/8=3,,30/x=3,x=30/3=10;=>8...10为一个区间。 
    ll ans=n*k;
   
    for(ll l=1,r;l<=n;l=r+1) {
        if(k/l!=0) r=min(k/(k/l),n); 
        else r=n;
        ans-=(k/l)*(r-l+1)*(l+r)/2;
       
    }
    printf("%lld",ans);
    return 0;
}

P2260 [清华集训2012]模积和

这个题目懒得写了。推一堆式子。难点在于要会求 1 2 + 2 2 + 2 3 + . . . + n 2 1^2+2^2+2^3+...+n^2 12+22+23+...+n2的通项。
查资料的得:
∑ i = 1 n i 2 = n ∗ ( n + 1 ) ( 2 n + 1 ) 6 \sum_{i=1}^ni^2=\frac{n*(n+1)(2n+1)}6 i=1ni2=6n(n+1)(2n+1)
不写了,这么多公式,真麻烦。

### Codeforces 平台上的整除分块题目及其解法 #### 1. 整除分块简介 整除分块是一种用于优化和表达式的技巧,特别适用于处理形如 $\sum_{i=1}^{n}\left\lfloor \frac{n}{i} \right\rfloor$ 的问题。通过观察可以发现,在一定范围内,$\left\lfloor \frac{n}{i} \right\rfloor$ 是相同的,因此可以通过分段来减少重复计算。 #### 2. 应用实例:Codeforces 616E - Sum of Remainders 该题要计算给定 $n$ 和 $m$ 下的 $\sum_{i=1}^{m}(n \% i)$[^3]。此题的核心在于如何高效地计算模运算的结果之和。利用整除分块的思想,可以将原问题转化为多个子区间内的快速和操作: ```cpp #include <bits/stdc++.h> using namespace std; long long sum_of_remainder(long long n, long long m) { long long result = 0; for (int l = 1; l <= min(n, m); ) { int r = min(m / ((n / l)), m); if (!r) break; // Calculate the contribution from this block. result += (r - l + 1LL) * (n % l); l = r + 1; } return result; } ``` 上述代码实现了基于整除分块的方法来解决这个问题。具体来说,`l` 表示当前区间的左边界,而 `r` 则表示右边界;每次迭代都会找到一个新的有效区间 `[l,r]` 来加速计算过程。 #### 3. 另一应用案例:Polycarp and Div 3 虽然这道题表面上看起来与整除无关,但实际上也可以借助类似的思维模式来进行解答。题目描述了一个长度为 $n$、元素取值于 $[1,k]$ 的序列,并询问满足某些条件下的方案数量。这里的关键点在于理解 GCD 性质以及组合计数原理的应用[^1]。 为了简化复杂度高的暴力枚举方法,同样可以考虑采用类似于整除分块的方式——即针对不同因子构建对应的贡献矩阵并累加其结果。不过需要注意的是,这类转换通常会涉及到更复杂的数学变换和技术细节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值