[聯合集訓6-12] Fraction 莫比烏斯反演+杜教篩+類歐幾里得
阿新 • • 發佈:2019-02-12
因為區間可以差分成字首,我們只用考慮的最簡真分數的個數。
莫比烏斯反演,
後面和式的上界只有
程式碼:
#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;
}