[小专题]除法分块
备份本地笔记
题目大概还是: [CQOI2007]余数之和-除法分块
题意:计算$\sum_{i=1}^n k mod i,n,k\leq 10^9$
关于除法分块
内容:对于形如$\sum_{i=1}^n f(\lfloor \frac{m}{i} \rfloor)$ 的函数进行求和,如果能$O(1)$地对$f(x)$求和,那么我们有$O(\sqrt m)$复杂度的方法对整个式子求和
- 证明:
- 对于$i<\sqrt m$的部分,显然至多只有$\sqrt m$个可能的$\lfloor \frac{m}{i}\rfloor$
- 对于$i\geq \sqrt m$,有$\lfloor \frac{m}{i}\rfloor \leq \sqrt m$,因此至多也只有$\sqrt m$个可能取值
- 因此整个$\lfloor \frac{m}{i} \rfloor$至多$2\sqrt m$种不同的值
- 所以总的复杂度还是$O(\sqrt m)$
- 另一方面,我们也能证明复杂度下界$\Omega (\sqrt m)$:
- 对于任意两个$l,r\leq \sqrt m$同时要$l\neq r,(l,r\in Z^+)$
- 若他们作为分母对应的值$\frac{m}{l}$与$\frac{m}{r}$**相同**,不妨设$l<r$,则有$\frac{m}{l}-\frac{m}{r}<1$,即$\frac{m(r-l)}{lr}<1$
- 得到$r-l<\frac{lr}{m}<1$,矛盾
- 因此对于$\sqrt m$以内的每个分母$x$,对应的$\lfloor\frac{m}{x}\rfloor$都是唯一的
- 到此我们就证明了这个算法的复杂度是$\Theta (\sqrt m)$的
- 那么问题来了,如何找到每一个连续区间?
- 换句话说,对于每个给定的左端点$i$,如何快速求出它的右端点?
for(int i=1,pos;i<=n;i=pos+1) { if(m/i)pos=min(n,m/(m/i));//k>i,防止分母为0 else pos=n; //pos 即右端点 }
- 右端点$r=\lfloor \frac{m}{\lfloor \frac{m}{i} \rfloor}\rfloor$ 。可以这么想:$\lfloor \frac{m}{i}\rfloor$是这一个**“块”**里对应的值$t$,如果把区间看成连续的,$\frac{m}{t}$再取整得到的正是满足这个块里值也为$t$的右端点,下取整则得到离散的区间对应的位置。
做法
回到这题,根据定义展开$\sum_{i=1}^n k mod i=nk-\sum_{i=1}^n \lfloor \frac{k}{i}\rfloor\times i$
后面的和式直接用除法分块求和
#include<bits/stdc++.h> using namespace std; typedef long long ll; ll n,k,ans; inline ll sum(ll x) { return x*(x+1)/2; } int main() { scanf("%lld%lld",&n,&k); ans=n*k; for(register ll i=1,pos;i<=n;i=pos+1) { if(k/i)pos=min(n,k/(k/i)); else pos=n; ans-=(k/i)*(sum(pos)-sum(i-1)); } printf("%lld",ans); return 0; }
时间复杂度$O(\sqrt k)$