CF1559E Mocha and Stars
前置知识
dp,容斥
思路
- 
首先观察到如果没有最后一个限制就非常的好做,就是一个背包问题。但是 \(\gcd\) 该怎么办?
 - 
长期学数论的都知道,在数论题中遇到 \(\gcd\) 时,你会掏出莫反尝试推式子。这到题,将题目形式化是
\[\sum_{a_1=l_1}^{r_1}\sum_{a_2=l_2}^{r_2}...\sum_{a_n=l_n}^{r_n}[\sum_{i=1}^na_i<=m][\gcd(a_1,a_2,a_3,..,a_n)=1] \]我们发现非常标准的莫反(所以本题还有一种莫反推式子的做法),但是我们可以不用,但是借鉴一些思路下来,处理 \(\gcd\) 时我们有一些套路,就是枚举 \(\gcd\) 的值是几,这里虽然告诉了 \(\gcd\) 是 \(1\) ,但我们不会算,所以我们先设 \(f_i\) 表示 \(\gcd=i\) 时上面的答案,最后我们要的就是 \(f_1\) 。
 - 
这样看似没有用不会求,但是我们可以再 \(g_i\) 表示 \(gcd=ki\) 时的答案,我们根据定义,可以列出 \(g,f\) 之间的式子, \(f_i=g_i-\sum_{j=2}^{m/i}f_{ji}\) 。这告诉我们算出 \(g\) 就能算出 \(f\) 。那么对于 \(g\) ,我们显然也是好求的,只要每次选的数都是 \(i\) 的倍数不就行了,做 dp 即可。
 
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=60,M=1e5+10,mod=998244353;
int n,m,l[N],r[N],f[N][M],g[M],ans[M];
void upd(int &x,int y){x=(x+y)%mod;return ;} 
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>l[i]>>r[i];
	for(int d=m/n;d>=1;d--)//计算是d的倍数的方案
	{
		for(int i=0;i<=n;i++)
			for(int j=0;j<=m/d+1;j++)f[i][j]=0;
		for(int i=0;i<=m/d+1;i++)f[0][i]=1;//初始化,f[0][0]等于一,但是前缀和
		for(int i=1;i<=n;i++)
		{
			int lt=l[i]/d,rt=r[i]/d;
			if(lt*d<l[i])lt++;//求出真正可以选的范围
			for(int j=lt;j<=m/d+1;j++)
			{
				int zb=max(j-rt,0ll);
				if(zb>j-lt)f[i][j]=0;
				else upd(f[i][j],(f[i-1][j-lt]-(zb?f[i-1][zb-1]:0)+mod)%mod);//注意越界 
				if(j)upd(f[i][j],f[i][j-1]);	
			}
		} 
		ans[d]=f[n][m/d];
		for(int i=2;i*d<=m;i++)upd(ans[d],mod-ans[i*d]);
		//cout<<g[d]<<" "<<ans[d]<<'\n';
	}
	cout<<ans[1]<<'\n';
} 

                
            
        
浙公网安备 33010602011771号