[小专题]除法分块

备份本地笔记




题目大概还是: [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)$

posted @ 2020-10-16 23:39  yoshinow2001  阅读(144)  评论(0编辑  收藏  举报