• 周一. 8月 8th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

【CF1559E】Mocha and Stars

admin

11月 28, 2021

题目

题目链接: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}ightfloor)

直接随便 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;
}

发表评论

您的电子邮箱地址不会被公开。