题目
题目链接:https://codeforces.com/contest/1559/problem/E
求有多少个长度为 (n) 的数组 (a) 满足:
- (l_ileq a_ileq r_i(iin [1,n]))。
- (sum^{n}_{i=1}a_ileq m)。
- (gcd(a_1,a_2,cdots,a_n)=1)。
(nleq 50,mleq 10^5)。
思路
首先很显然可以枚举 (gcd=d),然后问题转化为有多少个序列 (a) 满足 (left lceil frac{l_i}{d} ight ceil leq a_i leq left lfloor frac{r_i}{d} ight floor) 且 (sum^{n}_{i=1}a_ileq leftlfloorfrac{m}{d} ight floor)。
直接随便 dp 一下就好了。最后莫反一下就出答案了。
时间复杂度 (O(nmlog m))。如果整除分块的话似乎可以做到 (O(nm^{frac{3}{4}}))?
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=55,M=100010,MOD=998244353;
int n,m,a[N],l[N],r[N],prm[M],mu[M];
ll ans,f[M],g[M];
bool v[M];
void findprm(int n)
{
mu[1]=1;
for (int i=2;i<=n;i++)
{
if (!v[i]) prm[++m]=i,mu[i]=-1;
for (int j=1;j<=m;j++)
{
if (i>n/prm[j]) break;
v[i*prm[j]]=1; mu[i*prm[j]]=-mu[i];
if (!(i%prm[j])) { mu[i*prm[j]]=0; break; }
}
}
}
int main()
{
findprm(M-1);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
scanf("%d%d",&l[i],&r[i]);
for (int i=m;i>=1;i--)
{
f[0]=g[0]=1;
for (int j=1;j<=m/i;j++)
f[j]=0,g[j]=1;
for (int j=1;j<=n;j++)
{
int ql=(l[j]-1)/i+1,qr=r[j]/i;
if (ql>qr) { g[m/i]=0; break; }
for (int k=0;k<ql;k++) f[k]=0;
for (int k=ql;k<=m/i;k++)
f[k]=g[k-ql]-(k>qr ? g[k-qr-1] : 0);
g[0]=f[0];
for (int k=1;k<=m/i;k++)
g[k]=(g[k-1]+f[k])%MOD;
}
ans=(ans+mu[i]*g[m/i])%MOD;
}
cout<<(ans%MOD+MOD)%MOD;
return 0;
}