因为区间可以差分成前缀,我们只用考虑
≤ab
≤
a
b
的最简真分数
ji
j
i
的个数。
Ans=∑1≤j<i≤n[j≤aib][gcd(i,j)=1]
A
n
s
=
∑
1
≤
j
<
i
≤
n
[
j
≤
a
i
b
]
[
gcd
(
i
,
j
)
=
1
]
莫比乌斯反演,
∑1≤j<i≤n[j≤aib]∑d|gcd(i,j)μ(d)=∑d=1nμ(d)∑i=1⌊nd⌋∑j=1i−1[jd≤aidb]=∑d=1nμ(d)∑i=1⌊nd⌋⌊aib⌋
∑
1
≤
j
<
i
≤
n
[
j
≤
a
i
b
]
∑
d
|
gcd
(
i
,
j
)
μ
(
d
)
=
∑
d
=
1
n
μ
(
d
)
∑
i
=
1
⌊
n
d
⌋
∑
j
=
1
i
−
1
[
j
d
≤
a
i
d
b
]
=
∑
d
=
1
n
μ
(
d
)
∑
i
=
1
⌊
n
d
⌋
⌊
a
i
b
⌋
后面和式的上界只有 O(n−−√) 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;
}