1. 程式人生 > 其它 >「題解」最簡真分數

「題解」最簡真分數

本文將同步釋出於:

題目

題意簡述

給定兩個最簡真分數和正整數 \(\frac{a}{b},\frac{c}{d},n\),求有多少個最簡真分數 \(\frac{e}{f}\) 滿足 \(f\in[1,n]\)\(\frac{a}{b}\leq\frac{e}{f}\leq\frac{c}{d}\)

\(1\leq n\leq 10^{10}\)

題解

限制轉化

我們考慮列舉 \(f\),那麼 \(e\) 需要滿足的條件為 \(\left\lceil\frac{af}{b}\right\rceil\leq e\leq\left\lfloor\frac{cf}{d}\right\rfloor\)

\(\gcd(e,f)=1\)

不妨同統一形式,把 \(\left\lceil\frac{af}{b}\right\rceil\) 轉化為 \(\left\lfloor\frac{af+b-1}{b}\right\rfloor\)

我們要求解的答案即為:

\[\sum_{f=1}\sum^f_{e=1}\epsilon\left(\gcd(e,f)\right)\left[\left\lfloor\frac{af+b-1}{b}\right\rfloor\leq e\leq\left\lfloor\frac{cf}{d}\right\rfloor\right] \]\[\sum_{f=1}\sum^{\left\lfloor\frac{xf+y}{z}\right\rfloor}_{e=1}\epsilon\left(\gcd(e,f)\right) \]\[\sum^n_{d=1}\mu(d)\sum_{i=1}^{\left\lfloor\frac{n}{d}\right\rfloor}\left\lfloor\frac{xdi+y}{dz}\right\rfloor \]

這個顯然可以用數論分塊+杜教篩+類歐幾里得演算法做。

參考程式

#include<bits/stdc++.h>
using namespace std;
#define reg register
typedef long long ll;

bool st;

const int mod=998244353;

struct modInt{
	int x;
	inline modInt(reg int x=0):x(x){
		//assert(0<=x&&x<mod);
		return;
	}
	inline modInt operator+(const modInt& a)const{
		reg int sum=x+a.x;
		return sum>=mod?sum-mod:sum;
	}
	inline modInt operator-(const modInt& a)const{
		reg int sum=x-a.x;
		return sum<0?sum+mod:sum;
	}
	inline modInt operator-(void)const{
		return modInt(0)-*this;
	}
	inline modInt operator*(const modInt& a)const{
		return 1ll*x*a.x%mod;
	}
	inline void operator+=(const modInt& a){
		x+=a.x;
		if(x>=mod) x-=mod;
		return;
	}
	inline void operator-=(const modInt& a){
		x-=a.x;
		if(x<0) x+=mod;
		return;
	}
	inline void operator*=(const modInt& a){
		x=1ll*x*a.x%mod;
		return;
	}
};

inline modInt fpow(modInt x,reg int exp){
	modInt res=1;
	while(exp){
		if(exp&1)
			res*=x;
		x*=x,exp>>=1;
	}
	return res;
}

const modInt inv2=fpow(2,mod-2);

ll n;
int a,b,c,d;

const int MAXS=1e7+5;
const int S=1e7;

bool vis[MAXS];
int tot,prime[MAXS];
modInt mu[MAXS];
modInt Smu[MAXS];

inline void Init(reg int n){
	mu[1]=1;
	for(reg int i=2;i<=n;++i){
		if(!vis[i])
			prime[++tot]=i,mu[i]=-modInt(1);
		for(reg int j=1;j<=tot&&i*prime[j]<=n;++j){
			vis[i*prime[j]]=true;
			if(!(i%prime[j]))
				break;
			mu[i*prime[j]]=-mu[i];
		}
	}
	for(reg int i=1;i<=n;++i)
		Smu[i]=Smu[i-1]+mu[i];
	return;
}

unordered_map<ll,modInt> MSmu;

inline modInt getSmu(ll n){
	if(n<=S)
		return Smu[n];
	if(MSmu[n].x)
		return MSmu[n];
	modInt ans=1;
	for(reg ll l=2,r;l<=n;l=r+1){
		r=n/(n/l);
		ans-=modInt((r-l+1)%mod)*getSmu(n/l);
	}
	return MSmu[n]=ans;
}

inline modInt f(reg ll a,reg ll b,reg ll c,reg ll n){
	if((a*n+b)/c==0)
		return 0;
	if(!a)
		return modInt((n+1)%mod)*modInt((b/c)%mod);
	if(!n)
		return (b/c)%mod;
	if(a>=c||b>=c)
		return f(a%c,b%c,c,n)+modInt((a/c)%mod)*modInt(n%mod)*modInt((n+1)%mod)*inv2+modInt((b/c)%mod)*modInt((n+1)%mod);
	reg ll m=(a*n+b)/c;
	return modInt(n%mod)*modInt(m%mod)-f(c,c-b-1,a,m-1);
}

inline modInt getSum(reg ll n){
	modInt part1=n%mod;
	modInt part2=f(c,0,d,n);
	modInt part3=f(a,b-1,b,n);
	return part1+part2-part3;
}

bool ed;

int main(void){
	Init(S);
	int t;
	scanf("%d",&t);
	while(t--){
		scanf("%lld%d%d%d%d",&n,&a,&b,&c,&d);
		modInt ans=0;
		for(reg ll lef=1,rig;lef<=n;lef=rig+1){
			rig=n/(n/lef);
			ans+=(getSmu(rig)-getSmu(lef-1))*getSum(n/lef);
		}
		printf("%d\n",ans.x);
	}
	fprintf(stderr,"%.3lf s\n",1.0*clock()/CLOCKS_PER_SEC);
	fprintf(stderr,"%.3lf MiB\n",(&ed-&st)/1048576.0);
	return 0;
}