[清華集訓2012]模積和(數論分塊)
阿新 • • 發佈:2021-10-07
綜合性的數論分塊練習題,然而賽時並不知道平方數列求和公式……
;
交換求和符號順序,有 \(\sum\limits_{i=1}^{n}(n\mod i)\sum\limits_{j=1}^{m}(m\mod j)-\sum\limits_{i=1}^{n}(n\mod i)(m\mod i)\);
將取模轉換為下取整形式:\(\sum\limits_{i=1}^{n}(n-i \lfloor \frac{n}{i}\rfloor)\sum\limits_{j=1}^{m}(m-j \lfloor \frac{m}{j}\rfloor)-\sum\limits_{i=1}^{n}(n-i \lfloor \frac{n}{i}\rfloor)(m-j\lfloor \frac{m}{j}\rfloor)\) ;
拆開第二大項的括號,有\(\sum\limits_{i=1}^{n}(n-i \lfloor \frac{n}{i}\rfloor)\sum\limits_{j=1}^{m}(m-j \lfloor \frac{m}{j}\rfloor)-\sum\limits_{i=1}^{n}(n m-m i \lfloor \frac{n}{i}\rfloor-n i \lfloor \frac{m}{i}\rfloor+i^2\lfloor \frac{n}{i}\rfloor\lfloor \frac{m}{i}\rfloor)\)。有一個小問題:第二大項最後的 \(i^2\)
求 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[i\ne j](n\mod i)(m\mod j)\), 對 \(19940417\) 取模(至今未考證出是什麼日子)。\(1\le n,m\le 10^9\)。
先來看看這個式子有什麼特點,然後開拆:
- \([i\ne j]\) 看起來需要容斥掉;
- 取模似乎可以轉化為下取整,數論分塊——資料範圍驗證了這一點。
下面進行推導:
不妨設 \(n\le m\),原式可變形為 \(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}(n\mod i)(m\mod j)-\sum\limits_{i=1}^{n}(n\mod i)(m\mod i)\)
交換求和符號順序,有 \(\sum\limits_{i=1}^{n}(n\mod i)\sum\limits_{j=1}^{m}(m\mod j)-\sum\limits_{i=1}^{n}(n\mod i)(m\mod i)\);
將取模轉換為下取整形式:\(\sum\limits_{i=1}^{n}(n-i \lfloor \frac{n}{i}\rfloor)\sum\limits_{j=1}^{m}(m-j \lfloor \frac{m}{j}\rfloor)-\sum\limits_{i=1}^{n}(n-i \lfloor \frac{n}{i}\rfloor)(m-j\lfloor \frac{m}{j}\rfloor)\)
拆開第二大項的括號,有\(\sum\limits_{i=1}^{n}(n-i \lfloor \frac{n}{i}\rfloor)\sum\limits_{j=1}^{m}(m-j \lfloor \frac{m}{j}\rfloor)-\sum\limits_{i=1}^{n}(n m-m i \lfloor \frac{n}{i}\rfloor-n i \lfloor \frac{m}{i}\rfloor+i^2\lfloor \frac{n}{i}\rfloor\lfloor \frac{m}{i}\rfloor)\)。
不需要再拆了,這個又臭又長的式子已經可以用數論分塊處理了。
有一個小問題:第二大項最後的 \(i^2\) 怎麼處理?
平方和數列求和公式:\(\sum\limits_{i=1}^{n}=\frac{n(n+1)(2n+1)}{6}\)。數歸顯然可證。
下面是 AC 程式碼:
#define int long long
const int p=19940417;
int m1(int x){return (x*(x+1)>>1)%p;}//等差數列求和
int m2(int x){//平方和數列求和
return x%3==1?(x*(x+1)>>1)%p*((x<<1|1)/3)%p:(x*(x+1)/6)%p*(x<<1|1)%p;
}
int f(int x){//數論分塊,g()同
int ans=0;
for(int l=1,r,len;l<=x;l=r+1){
r=x/(x/l),len=r-l+1;
(ans+=len*x%p-(x/l)%p*(m1(r)-m1(l-1)))%=p;
}
return (ans+p)%p;
}
int g(int x,int y){
int ans=0;
for(int l=1,r,len;l<=x&&l<=y;l=r+1){
r=min(x/(x/l),y/(y/l)),len=r-l+1;//因為有兩個下取整相乘,故取min保證正確性
(ans+=x*y%p*len)%=p;
(ans-=(x*(y/l)+y*(x/l))%p*(m1(r)-m1(l-1)))%=p;
(ans+=(x/l)*(y/l)%p*(m2(r)-m2(l-1)))%=p;
}
return (ans+p)%p;
}
signed main(){
int n=rd(),m=rd();
printf("%lld\n",(f(n)*f(m)%p-g(n,m)+p)%p);
return 0;
}