P2260 [清华集训2012]模积和 题解

CSDN同步

原题链接

简要题意:

给定 \(n,m\),求:

\[\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,m \leq 10^9\).

我们直接奔着 \(100\) 分的数据去吧,不要看部分分,部分分就没意思。

这个式子的瓶颈在于 \(n \space \text{mod} \space i\) 的展开问题。所以只需要用 \(n \space \text{mod} \space i = n - \lfloor \frac{n}{i} \rfloor \times i\) ,然后灵活用多项式的拆开与合并,一波整除分块带走即可。

首先说好,这一次的推式子没有 莫比乌斯反演,也没有 奇怪的筛法,有的只是 多项式的灵活展开 与 整除分块

于是我们开始推式子。

\[\sum_{i=1}^n \sum_{j=1}^m (n \space \text{mod} \space i) \times (m \space \text{mod} \space j) , i \not = j \]

\[= \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 \]

\[= \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=j\) 的答案丢掉,先做所有的答案。

\(f_n = \sum_{i=1}^n (n - \lfloor \frac{n}{i} \rfloor \times i)\),则 \(\text{ans} = f_n \times f_m\),考虑如何快速求 \(f\).

\[f_n = \sum_{i=1}^n (n - \lfloor \frac{n}{i} \rfloor \times i) \]

\[= n^2 - \sum_{i=1}^n \Big( \lfloor \frac{n}{i} \rfloor \times i \Big) \]

然后你会发现这东西直接 整除分块\(\mathcal{O}( \sqrt{n})\) 很稳。

最后多余的 \(i=j\) 的答案应该会是:

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

\[= \sum_{i=1}^{\min(n,m)} (n - \lfloor \frac{n}{i} \rfloor \times i) \times (m - \lfloor \frac{m}{i} \rfloor \times i) \]

\[= \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) \]

\[= 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 \]

\(g_{n,k} = \sum_{i=1}^k i \lfloor \frac{n}{i} \rfloor\)

则:

\[= 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 \]

显然,\(g\) 可以整除分块,所以整个式子都可以整除分块。

对于最后的一块,我们令 \(h_{n,m,k} = \sum_{i=1}^{k} i^2 \lfloor \frac{n}{i} \rfloor \lfloor \frac{m}{i} \rfloor\),但是注意到:

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

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

但是模意义下的除法不好做。这里有三种解决方法:

  • 求出模意义下 \(6\) 的逆元,可惜模数不是质数,我们只能用 \(\text{exgcd}\) 去做。
  • 由于 \(n(n+1)\) 不会溢出,可以考虑 \(n(n+1)/2 * (2n+1)/3\),但是最终的结果会爆 \(\text{long long}\),因此这种方法不行。
  • 直接开 \(\text{\_\_int128}\) 解决所有问题。

当然本人为了方便直接开了 \(\text{\_\_int128}\),解决了所有的溢出计算问题。反正 \((10^9)^3 = 10^{27}\) 肯定不会超过 \(2^{127}\),因为 \(2^{127}\) 大概有 \(40\) 位。
上述方法已经说明,\(f\)\(g\) 均可以在 \(\mathcal{O}(\sqrt{n} + \sqrt{m} + \sqrt{\min(n,m)})\) 的时间内解决。\(n\)\(m\)\(\min(n,m)\) 均同级,所以最终时间复杂度为 \(\mathcal{O}(\sqrt{n})\).

时间复杂度:\(\mathcal{O}(\sqrt 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'));
} //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;
}


posted @ 2020-06-26 18:30  bifanwen  阅读(172)  评论(0编辑  收藏  举报