P2260 [清華集訓2012]模積和 題解

博客園同步

原題鏈接

簡要題意:

給定 n,mn,m,求:

i=1nj=1m(n mod i)×(m mod j),ij\sum_{i=1}^n \sum_{j=1}^m (n \space \text{mod} \space i) \times (m \space \text{mod} \space j) , i \not = j

n,m109n,m \leq 10^9.

我們直接奔着 100100 分的數據去吧,不要看部分分,部分分就沒意思。

這個式子的瓶頸在於 n mod in \space \text{mod} \space i 的展開問題。所以只需要用 n mod i=nni×in \space \text{mod} \space i = n - \lfloor \frac{n}{i} \rfloor \times i ,然後靈活用多項式的拆開與合併,一波整除分塊帶走即可。

首先說好,這一次的推式子沒有 莫比烏斯反演,也沒有 奇怪的篩法,有的只是 多項式的靈活展開 與 整除分塊

於是我們開始推式子。

i=1nj=1m(n mod i)×(m mod j),ij\sum_{i=1}^n \sum_{j=1}^m (n \space \text{mod} \space i) \times (m \space \text{mod} \space j) , i \not = j

=i=1nj=1m(nni×i)×(mmj×j),ij = \sum_{i=1}^n \sum_{j=1}^m (n - \lfloor \frac{n}{i} \rfloor \times i) \times (m - \lfloor \frac{m}{j} \rfloor \times j) , i \not = j

=i=1n(nni×i)×j=1m(mmj×j),ij = \sum_{i=1}^n (n - \lfloor \frac{n}{i} \rfloor \times i) \times \sum_{j=1}^m (m - \lfloor \frac{m}{j} \rfloor \times j) , i \not = j

首先我們把 i=ji=j 的答案丟掉,先做所有的答案。

fn=i=1n(nni×i)f_n = \sum_{i=1}^n (n - \lfloor \frac{n}{i} \rfloor \times i),則 ans=fn×fm\text{ans} = f_n \times f_m,考慮如何快速求 ff.

fn=i=1n(nni×i)f_n = \sum_{i=1}^n (n - \lfloor \frac{n}{i} \rfloor \times i)

=n2i=1n(ni×i) = n^2 - \sum_{i=1}^n \Big( \lfloor \frac{n}{i} \rfloor \times i \Big)

然後你會發現這東西直接 整除分塊O(n)\mathcal{O}( \sqrt{n}) 很穩。

最後多餘的 i=ji=j 的答案應該會是:

i=1min(n,m)(n mod i)×(m mod i)\sum_{i=1}^{\min(n,m)} (n \space \text{mod} \space i) \times (m \space \text{mod} \space i)

=i=1min(n,m)(nni×i)×(mmi×i) = \sum_{i=1}^{\min(n,m)} (n - \lfloor \frac{n}{i} \rfloor \times i) \times (m - \lfloor \frac{m}{i} \rfloor \times i)

=i=1min(n,m)(nmnimimini+i2nimi) = \sum_{i=1}^{\min(n,m)} \Big( nm - ni \lfloor \frac{m}{i} \rfloor - mi \lfloor \frac{n}{i} \rfloor + i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor \Big)

=nmmin(n,m)n×i=1min(n,m)imim×i=1min(n,m)ini+i=1min(n,m)i2nimi = nm \cdot \min(n,m) - n \times \sum_{i=1}^{\min(n,m)} i \lfloor \frac{m}{i} \rfloor - m \times \sum_{i=1}^{\min(n,m)} i \lfloor \frac{n}{i} \rfloor + \sum_{i=1}^{\min(n,m)} i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor

gn,k=i=1kinig_{n,k} = \sum_{i=1}^k i \lfloor \frac{n}{i} \rfloor

則:

=nmmin(n,m)n×gm,min(n,m)m×gn,min(n,m)+i=1min(n,m)i2nimi = nm \cdot \min(n,m) - n \times g_{m,\min(n,m)} - m \times g_{n,\min(n,m)} + \sum_{i=1}^{\min(n,m)} i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor

顯然,gg 可以整除分塊,所以整個式子都可以整除分塊。

對於最後的一塊,我們令 hn,m,k=i=1ki2nimih_{n,m,k} = \sum_{i=1}^{k} i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor,但是注意到:

i=1min(n,m)i2nimi\sum_{i=1}^{\min(n,m)} i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor 的計算需要用到公式:

i=1ni2=n(n+1)(2n+1)6\sum_{i=1}^n i^2 = \frac{n (n+1) (2n+1)}{6}

但是模意義下的除法不好做。這裏有三種解決方法:

  • 求出模意義下 66 的逆元,可惜模數不是質數,我們只能用 exgcd\text{exgcd} 去做。
  • 由於 n(n+1)n(n+1) 不會溢出,可以考慮 n(n+1)/2(2n+1)/3n(n+1)/2 * (2n+1)/3,但是最終的結果會爆 long long\text{long long},因此這種方法不行。
  • 直接開 __int128\text{\_\_int128} 解決所有問題。

當然本人爲了方便直接開了 __int128\text{\_\_int128},解決了所有的溢出計算問題。反正 (109)3=1027(10^9)^3 = 10^{27} 肯定不會超過 21272^{127},因爲 21272^{127} 大概有 4040 位。
上述方法已經說明,ffgg 均可以在 O(n+m+min(n,m))\mathcal{O}(\sqrt{n} + \sqrt{m} + \sqrt{\min(n,m)}) 的時間內解決。nnmmmin(n,m)\min(n,m) 均同級,所以最終時間複雜度爲 O(n)\mathcal{O}(\sqrt{n}).

時間複雜度:O(n)\mathcal{O}(\sqrt n).

實際得分:100pts100pts.

這個故事告訴我們,清華集訓的題並不難,在自己擅長的領域完全可以吊打集訓隊。(當然離那一天還很遙遠)

#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'));
} //int128 需要快讀快輸
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;
	} //printf("%lld\n",ans);
	return (n*n-ans)%MOD;
} //整除分塊計算 f

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); //保證塊不超過 k
		ans=(ans+(n/l)*(sum(r)-sum(l-1))%MOD)%MOD;
	} return ans;
} //整除分塊計算 g

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); //保證塊不超過 k
		ans=(ans+(n/l)*(m/l)%MOD*(pf(r)-pf(l-1))%MOD)%MOD;
	} return ans;
} //整除分塊計算 h

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),putchar(' '),write(tot2),putchar('\n');
	write((tot1-tot2+MOD)%MOD); //減法需要處理負數
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章