博客園同步
原題鏈接
簡要題意:
給定 n,m,求:
i=1∑nj=1∑m(n mod i)×(m mod j),i=j
n,m≤109.
我們直接奔着 100 分的數據去吧,不要看部分分,部分分就沒意思。
這個式子的瓶頸在於 n mod i 的展開問題。所以只需要用 n mod i=n−⌊in⌋×i ,然後靈活用多項式的拆開與合併,一波整除分塊帶走即可。
首先說好,這一次的推式子沒有 莫比烏斯反演,也沒有 奇怪的篩法,有的只是 多項式的靈活展開 與 整除分塊。
於是我們開始推式子。
i=1∑nj=1∑m(n mod i)×(m mod j),i=j
=i=1∑nj=1∑m(n−⌊in⌋×i)×(m−⌊jm⌋×j),i=j
=i=1∑n(n−⌊in⌋×i)×j=1∑m(m−⌊jm⌋×j),i=j
首先我們把 i=j 的答案丟掉,先做所有的答案。
令 fn=∑i=1n(n−⌊in⌋×i),則 ans=fn×fm,考慮如何快速求 f.
fn=i=1∑n(n−⌊in⌋×i)
=n2−i=1∑n(⌊in⌋×i)
然後你會發現這東西直接 整除分塊,O(n) 很穩。
最後多餘的 i=j 的答案應該會是:
i=1∑min(n,m)(n mod i)×(m mod i)
=i=1∑min(n,m)(n−⌊in⌋×i)×(m−⌊im⌋×i)
=i=1∑min(n,m)(nm−ni⌊im⌋−mi⌊in⌋+i2⌊in⌋⌊im⌋)
=nm⋅min(n,m)−n×i=1∑min(n,m)i⌊im⌋−m×i=1∑min(n,m)i⌊in⌋+i=1∑min(n,m)i2⌊in⌋⌊im⌋
令 gn,k=∑i=1ki⌊in⌋
則:
=nm⋅min(n,m)−n×gm,min(n,m)−m×gn,min(n,m)+i=1∑min(n,m)i2⌊in⌋⌊im⌋
顯然,g 可以整除分塊,所以整個式子都可以整除分塊。
對於最後的一塊,我們令 hn,m,k=∑i=1ki2⌊in⌋⌊im⌋,但是注意到:
∑i=1min(n,m)i2⌊in⌋⌊im⌋ 的計算需要用到公式:
i=1∑ni2=6n(n+1)(2n+1)
但是模意義下的除法不好做。這裏有三種解決方法:
- 求出模意義下 6 的逆元,可惜模數不是質數,我們只能用 exgcd 去做。
- 由於 n(n+1) 不會溢出,可以考慮 n(n+1)/2∗(2n+1)/3,但是最終的結果會爆 long long,因此這種方法不行。
- 直接開 __int128 解決所有問題。
當然本人爲了方便直接開了 __int128,解決了所有的溢出計算問題。反正 (109)3=1027 肯定不會超過 2127,因爲 2127 大概有 40 位。
上述方法已經說明,f 和 g 均可以在 O(n+m+min(n,m)) 的時間內解決。n ,m 與 min(n,m) 均同級,所以最終時間複雜度爲 O(n).
時間複雜度:O(n).
實際得分:100pts.
這個故事告訴我們,清華集訓的題並不難,在自己擅長的領域完全可以吊打集訓隊。(當然離那一天還很遙遠)
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef __int128 ll;
const ll MOD=19940417;
inline ll read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
ll x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}
inline void write(ll x) {
if(x<0) {putchar('-');write(-x);return;}
if(x<10) {putchar(char(x%10+'0'));return;}
write(x/10);putchar(char(x%10+'0'));
}
inline ll sum(ll n) {return n*(n+1)/2%MOD;}
inline ll pf(ll n) {return n*(n+1)*(2*n+1)/6%MOD;}
inline ll min(ll n,ll m) {return n<m?n:m;}
inline ll f(ll n) {
ll ans=0;
for(ll l=1,r;l<=n;l=r+1) {
r=n/(n/l);
ans=(ans+(n/l)*(sum(r)-sum(l-1))%MOD)%MOD;
}
return (n*n-ans)%MOD;
}
inline ll g(ll n,ll k) {
ll ans=0;
for(ll l=1,r;l<=k;l=r+1) {
r=n/(n/l); r=min(r,k);
ans=(ans+(n/l)*(sum(r)-sum(l-1))%MOD)%MOD;
} return ans;
}
inline ll h(ll n,ll m,ll k) {
ll ans=0;
for(ll l=1,r;l<=k;l=r+1) {
r=min(n/(n/l),m/(m/l)); r=min(r,k);
ans=(ans+(n/l)*(m/l)%MOD*(pf(r)-pf(l-1))%MOD)%MOD;
} return ans;
}
int main() {
ll n=read(),m=read(),x=min(n,m);
ll tot1=f(n)*f(m)%MOD;
ll tot2=(n*m*min(n,m)-n*g(m,x)-m*g(n,x)+h(n,m,x)+MOD+MOD)%MOD;
write((tot1-tot2+MOD)%MOD);
return 0;
}