【题解】P2260 [清华集训2012]模积和(数学,整除分块)
【题解】P2260 [清华集训2012]模积和
比较简单的一道推式子的题。(清华集训居然会出这种水题的吗)
题目链接
题意概述
求
\[\sum_{i=1}^{n} \sum_{j=1}^{m} (n \bmod i) \times (m \bmod j), i \neq j
\]
\(\mod 19940417\) 的值
思路分析
直接容斥一下然后推式子即可,见下:
\[\begin{aligned}\sum \limits_{i=1}^n \sum \limits_{j=1}^m(n\bmod i)(m\bmod j)
&=\sum \limits_{i=1}^n (n \bmod i)\sum \limits_{j=1}^m (m \bmod j)-\sum \limits_{i=1}^{\min(n,m)} (n\bmod i)(m \bmod j)\\
&=\sum \limits_{i=1}^n (n \bmod i) \sum \limits_{j=1}^m (m-\left\lfloor\dfrac{m}{j}\right\rfloor \times j)-\sum \limits_{i=1}^{\min(n,m)}(n-\left\lfloor\dfrac{n}{i}\right\rfloor\times i)(m-\left\lfloor\dfrac{m}{i}\right\rfloor\times i)\\
&=\sum \limits_{i=1}^n (n \bmod i) (\sum \limits_{j=1}^mm -\sum \limits_{j=1}^m\left\lfloor\dfrac{m}{j}\right\rfloor \times j)-\sum \limits_{i=1}^{\min(n,m)}(nm-n\left\lfloor\dfrac{m}{i}\right\rfloor\times i-m\left\lfloor\dfrac{n}{i}\right\rfloor\times i+\left\lfloor\dfrac{m}{i}\right\rfloor\left\lfloor\dfrac{n}{i}\right\rfloor\times i^2)\\
&=\sum \limits_{i=1}^n (n \bmod i)(m^2-\sum \limits_{j=1}^m\left\lfloor\dfrac{m}{j}\right\rfloor \times j)-\sum \limits_{i=1}^{\min(n,m)}nm+n\sum \limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{m}{i}\right\rfloor \times i+m\sum \limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \times i-\sum\limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor \times i^2\\
&=(m^2-\sum \limits_{j=1}^m\left\lfloor\dfrac{m}{j}\right\rfloor \times j)\sum \limits_{i=1}^n (n\bmod i)-\min(n,m)nm+n\sum \limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{m}{i}\right\rfloor \times i+m\sum \limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \times i-\sum\limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor \times i^2\\
&=(m^2-\sum \limits_{j=1}^m\left\lfloor\dfrac{m}{j}\right\rfloor \times j)(n^2-\sum \limits_{i=1}^n\left\lfloor\dfrac{n}{i}\right\rfloor\times i)-\min(n,m)nm+n\sum \limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{m}{i}\right\rfloor \times i+m\sum \limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \times i-\sum\limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor \times i^2\\ \end{aligned}
\]
发现前四项都可以整除分块随便求。
我们考虑最后一项怎么求。
首先考虑 \(\sum\limits_{i=1}^{\min(n,m)}\left\lfloor\dfrac{n}{i}\right\rfloor \left\lfloor\dfrac{m}{i}\right\rfloor\),这实际上也可以整除分块,只不过需要二维整除分块。
其实它和一般的整除分块区别也不大,很显然,每次只需要改一下块长就好了。
具体地,每次求 \(r\) 时:
\[r=\min(\left\lfloor\dfrac{n}{\left\lfloor \dfrac{n}{l}\right\rfloor}\right\rfloor,\left\lfloor\dfrac{m}{\left\lfloor \dfrac{m}{l}\right\rfloor}\right\rfloor)
\]
时间复杂度是 \(O(\sqrt \min(n,m))\)。
然后考虑 \(\sum \limits_{i=1}^{\min(n,m)} i^2\) 怎么求。
有一个结论:
\[1^2+2^2+3^2+\cdots+n^2=\dfrac{n(n+1)(2n+1)}{6}
\]
所以我们就可以求出整个的式子了。
易错点
\(19940417\) 不是素数,所以请注意:
模意义下计算 \(x\) 的逆元只有当 \(\bmod\) 为素数时才能使用 \(qpow(x,mod-2)\)!!!
模意义下计算 \(x\) 的逆元只有当 \(\bmod\) 为素数时才能使用 \(qpow(x,mod-2)\)!!!
模意义下计算 \(x\) 的逆元只有当 \(\bmod\) 为素数时才能使用 \(qpow(x,mod-2)\)!!!
我是不会告诉你我在这挂了一个小时的……
所以可以使用 exgcd 预处理出来 \(2\) 和 \(6\) 的逆元,然后求解。
代码实现
//luoguP2260
#include<iostream>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int mod=19940417,inv2=9970209,inv6=3323403;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int qpow(int a,int T)
{
int ret=1;
while(T)
{
if(T&1)(ret*=a)%=mod;
(a*=a)%=mod;T>>=1;
}
// cout<<ret<<endl;
return ret;
}
int work(int n,int k)
{
int ret=0;
for(int l=1,r;l<=n;l=r+1)
{
if(k/l==0)break;
r=min(k/(k/l),n);
(ret+=(k/l)*(l+r)%mod*(r-l+1)%mod*inv2)%=mod;
}
return ret;
}
int get(int n)
{
return n*(n+1)%mod*(2*n+1)%mod*inv6%mod;
}
int work2(int n,int k,int p)
{
int ret=0;
for(int l=1,r;l<=n;l=r+1)
{
r=min({k/(k/l),p/(p/l),n});
(ret+=(k/l)*(p/l)%mod*(get(r)-get(l-1)+mod)%mod)%=mod;
}
return ret;
}
signed main()
{
int n,m;
n=read();m=read();
int sumn=work(n,n),summ=work(m,m);
int sumM=work(min(n,m),m),sumN=work(min(n,m),n);
int ans1=(n*n%mod-sumn+mod)%mod*(m*m%mod-summ+mod)%mod;
int ans2=(min(n,m)*n%mod*m%mod+mod)%mod;
int ans3=n*sumM%mod;
int ans4=m*sumN%mod;
int ans5=work2(min(n,m),n,m);
cout<<((((ans1-ans2+mod)%mod+ans3)%mod+ans4)%mod-ans5+mod)%mod<<"\n";
return 0;
}