淺談整除分塊

淺談整除分塊

前言

我們在學習整除分塊之前,首先你得整除分塊就是是個什麼,它跟分塊(區間操作)相似但是不同(我學的時候有點小懵一直以爲是分塊然後額)。
我是在學習莫比烏斯反演的時候看到要先學前置知識整除分塊,於是去學習。(整除分塊比狄利克雷卷積簡單多了,雖然我到現在還是不會狄利克雷卷積和莫比烏斯反演。)

例題

洛谷
餘數求和
給出正整數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 mod 3 + 5 mod 4 + 5 mod 5 …… + 5 mod 10=0+1+2+1+0+5+5+5+5+5=29

推導題意可得
i=1nkmod  i\sum_{i=1}^{n} {k\mod i}
首先數據之大讓你沒有辦法去暴力,所以它的難度是提高+省選-,那麼再次推導,可得式子爲
=i=1nki(k/i)=\sum_{i=1}^{n}{k-i*(k/i)}
=nki=1ni(k/i)=n*k-\sum_{i=1}^{n}{i*(k/i)}
到此爲止,就是整除分塊的模板了,即求i=1ni(k/i)\sum_{i=1}^{n}{i*(k/i)},當然k/ik/i向下取整

到此,離開本題去講整除分塊的模板

整除分塊

i=1nn/i\sum_{i=1}^{n}{n/i}
以下爲模板

#include<bits/stdc++.h>
using namespace std;
long long n,ans;
int main()
{
  scanf("%lld",&n);
  for(int l=1,r;l<=n;l=r+1)
     {
        r=n/(n/l);
        ans+=(r-l+1)*(n/l);
        cout<<l<<' '<<r<<' '<<ans<<endl;
     }
  printf("%lld",ans);
}

當然給模板是爲了不用手動打樣例
我們通過模板可以得出
在這裏插入圖片描述

這組數據是怎麼的出來了的呢?

推導過程

n=10n=10時,我們手動求i=1nn/i\sum_{i=1}^{n}{n/i}
在這裏插入圖片描述

可知答案是27,與上面相同,那麼這是爲什麼?
整除分塊,是把nn除以每一個ii的商相同的分成一塊
由10的樣例可知
重複的商相同的部分,我們可以在O(1)的時間內求出來
枚舉(l,r)(l,r)
(l,r)(l,r)區間即對於該區間任何一個數來說,nn \ i=ni=n \ ll
在這裏插入圖片描述
我們從1開始枚舉ll
r=n/(n/l)r=n/(n/l)
然後l=r+1l=r+1
這樣可以把該序列整除分塊了
然後整除分塊的時間即從O(n)縮減到了O(1)

回到例題

我們在把=nki=1ni(k/i)=n*k-\sum_{i=1}^{n}{i*(k/i)}推導一下
可以得到對於每個區間(l,r)(l,r)來說,公式爲(k/l)(rl+1)(l+r)/2(k/l)*(r-l+1)*(l+r)/2(自己推一推,很快滴)
即可得到答案

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

int main() {
    ll n,k;
    scanf("%lld%lld",&n,&k);
    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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章