1. 程式人生 > >[聯合集訓6-12] Fraction 莫比烏斯反演+杜教篩+類歐幾里得

[聯合集訓6-12] Fraction 莫比烏斯反演+杜教篩+類歐幾里得

因為區間可以差分成字首,我們只用考慮ab的最簡真分數ji的個數。

Ans=1j<in[jaib][gcd(i,j)=1]
莫比烏斯反演,
1j<in[jaib]d|gcd(i,j)μ(d)=d=1nμ(d)i=1ndj=1i1[jdaidb]=d=1nμ(d)i=1ndaib
後面和式的上界只有
O(n)
種,前面用杜教篩篩出μ的和,後面用類歐幾里得算即可。
程式碼:
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
#define N 10000010
#define up(x,y) (x=(x+(y))%mod)
using namespace std;
const int mod=998244353;
int pri[N/10],num,mu[N],sm[N],i2=(mod+1)/2;
const int R=1000000;
struct ha
{
    int
cnt,hd[R+5],nxt[R]; ll a[R],b[R]; void add(ll x,ll y) { int id=x%R; a[++cnt]=x;b[cnt]=y; nxt[cnt]=hd[id];hd[id]=cnt; } ll qry(ll x) { int id=x%R; for(int p=hd[id];p;p=nxt[p]) if(a[p]==x) return b[p]; return 0; } }H; void getpri(int
n) { memset(mu,0x3f,sizeof(mu)); mu[1]=1; for(int i=2;i<=n;i++) { if(mu[i]>1) pri[++num]=i,mu[i]=-1; for(int j=1;j<=num&&i*pri[j]<=n;j++) { if(i%pri[j]==0) {mu[i*pri[j]]=0;break;} mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=n;i++) sm[i]=(mu[i]+sm[i-1])%mod; } ll qm(ll n) { if(n<=10000000) return sm[n]; ll tmp=H.qry(n); if(tmp) return tmp; ll re=1; for(ll l=2,r;l<=n;l=r+1) { ll x=n/l;r=n/x; up(re,-(r-l+1)%mod*qm(x)); } H.add(n,re); return re; } ll likegcd(ll n,ll a,ll b,ll c) { ll tn=n%mod; ll re=(tn*(tn+1)%mod*i2%mod*(a/b)%mod+(tn+1)*(c/b)%mod)%mod; a%=b;c%=b;ll m=(n*a+c)/b; if(a==0) return re; return (re+tn*m%mod-likegcd(m-1,b,a,b-c-1)+mod)%mod; } ll solve(ll n,ll a,ll b) { ll re=0; for(ll l=1,r;l<=n;l=r+1) { ll x=n/l;r=n/x; up(re,likegcd(x,a,b,0)*(qm(r)-qm(l-1)+mod)%mod); } return re; } int main() { int ca,a,b,c,d; ll ans,n; getpri(10000000); scanf("%d",&ca); while(ca--) { scanf("%lld%d%d%d%d",&n,&a,&b,&c,&d); ll ans=solve(n,c,d)-solve(n,a,b)+(b<=n); ans=(ans%mod+mod)%mod; printf("%lld\n",ans); } return 0; }