事情是這樣的,我驗了這一場 CF。顯然我玩原神玩多了有一個很奇怪的、不能過的演算法,哦,當然,在我本機可以過。為了展現自己的智慧糖,我寫一下。
出題人是先發給我了一個限制都是 \(n\) 的,因此只有這個。\(n,m\) 改改就是了。
要求 \(1\le a\le n,1\le b\le n\) 滿足
- \(a+b\mid b\times \gcd(a,b)\) 的個數。
\[a+b\mid b\times \gcd(a,b)\Leftrightarrow \frac{a+b}{\gcd(a,b)}\mid b
\]
設 \(a=p_ag,b=p_bg\),其中 \(g=\gcd(a,b)\)。則
\[\Leftrightarrow p_a+p_b\mid gp_b
\]
則
\[k(p_a+p_b)=gp_b\implies kp_a=(g-k)p_b
\]
因為 \(p_a,p_b\) 互質,則 \(k=\alpha p_b,g-k=\alpha p_a\),則 \(g=\alpha(p_a+p_b)\)。
則
\[\Leftrightarrow p_a+p_b\mid g \Leftrightarrow \frac{a+b}{\gcd(a,b)}\mid \gcd(a,b)
\]
所以可以列舉 \(\gcd(a,b)=g\),我們就要求 \(p_a,p_b\in \mathbb{N}^+\) 滿足
- \(p_a+p_b-\lfloor \frac{n}{\gcd(a,b)}\rfloor \le p_a,pb\le \lfloor \frac{n}{\gcd(a,b)}\rfloor\)。
- \(\gcd(p_a,p_b)=1\)。
我們列舉 \(p_a+p_b\) 的值是什麼,\(\mathcal{O}(n\ln n)\)。則我們要求:
設 \(l=p_a+p_b,s=l-\lfloor \frac{n}{\gcd(a,b)}\rfloor,t=\min(\frac{n}{\gcd(a,b)},l-1)\)。
\[\begin{aligned}
&\sum_{p_a=s,p_b=l-p_1}^{p_a\sim t} [\gcd(p_a,p_b)=1] \\
=& \ \sum_{p_a=s}^t \sum_{d\mid \gcd(p_a,l-p_a)}\mu(d) \\
=& \ \sum_{d} \mu(d) \sum_{p_a=s}^t [d\mid p_a][d\mid l-p_a] \\
=& \ \sum_{d} \mu(d) \sum_{p_a=s}^t [d\mid p_a][d\mid l]\\
=& \ \sum_{d\mid l} \mu(d) \sum_{p_a=s}^t [d\mid p_a] \\
=& \ \sum_{d\mid l} \mu(d) (\lfloor \frac{t}{d} \rfloor-\lfloor \frac{s-1}{d} \rfloor) \\
\end{aligned}
\]
這個我們列舉 \(d\) 就可以了。時間複雜度 \(\mathcal{O}(n\ln\ln n)\)。
鑑定為最近學莫比烏斯函式被魔怔了。以下是 \(n,n\) 的 code:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6+6;
ll pr[N],cnt,mu[N],vis[N];
vector<int> fac[N];
void init(){
mu[1]=1;
for (int i=1; i<N; i++){
for (int j=i; j<N; j+=i){
fac[j].push_back(i);
}
}
for (int i=2; i<N; i++){
if (!vis[i]){
mu[i]=-1;
pr[++cnt]=i;
}
for (int j=1; j<=cnt && i*pr[j]<N; j++){
vis[i*pr[j]]=1;
if (i%pr[j]==0){
break;
}
else{
mu[i*pr[j]]=-mu[i];
}
}
}
}
ll n,p;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
init();
cin>>n>>p;
ll ans=0;
for (int gc=1; gc<=n; gc++){
// (a+b)/gc | gc
for (auto u : fac[gc]){
if (u==gc){
continue;
}
int s=n/gc;
// 1<=pa,pb<=s
// gcd(pa,pb)=1
int l=gc/u;
// pa+pb=l
s=min(s,l-1);
int ss=l-s;
if (s*2<l){
continue;
}
for (auto v : fac[l]){
ans+=mu[v]*((ll)(s/v)-(ll)((ss-1)/v));
}
}
}
cout<<ans%p<<"\n";
return 0;
}
哦這隻能說明我數學不好。為了防止大家被魔怔,我貼一個出題人發我的正解。
其實有一定的相同,就是後面的列舉不同。