Reverse Card (Hard Version)

SFlyer發表於2024-05-01

事情是這樣的,我驗了這一場 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;
}

哦這隻能說明我數學不好。為了防止大家被魔怔,我貼一個出題人發我的正解。

image

其實有一定的相同,就是後面的列舉不同。

相關文章