P7575 「PMOI-3」公约数 题解

考虑 dp,记 \(dp_{i,j}\) 表示确定前 \(i\) 个数最后一个数为 \(j\) 的方案数,则:

\[dp_{1,i}=[x_1\mid i]\\ dp_{i,j}=[x_{i-1}\mid j]\sum_{k=1}^m dp_{i-1,k}[\gcd(k,j)=x_{i-1}][x_{i-1}\mid k]\\ dp_{i,j}=[x_{i-1}\mid j]\sum_{k=1}^m\sum_{d\mid (j/x_{i-1}),d\mid (k/x_{i-1})}\mu(d)\cdot dp_{i-1,k} \]

\[\mathbf{Answer}=\sum_{x_{n-1}|i}dp_{n,i} \]

套个莫比乌斯反演:

\[dp_{i,j\cdot x_{i-1}}=\sum_{d|j}\mu(d)\sum_{k=1}^{m/x_{i-1}/d}dp_{i-1,kd\cdot x_{i-1}}\\ \]

发现 \(x_i\) 互不相同,于是 \(\sum \frac m {x_i}\) 是可以接受的,先对 \( \sum_k\) 的部分做一遍狄利克雷后缀和,再对 \(\sum_d\) 的部分做一遍狄利克雷前缀和即可,时间复杂度 \(\mathcal O(m\log m\log\log m)\)

参考代码:

#include<bits/stdc++.h>
#define ll long long
#define mxn 1000003
#define md 998244353
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,m,t,ans,f[mxn],s[mxn],a[mxn],d[mxn],p[mxn>>1],mu[mxn];
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
inline void add(int &x,int y){
	x+=y;if(x>=md)x-=md;
} 
signed main(){
	n=read(),m=read();
	mu[1]=1;
	rep(i,2,m){
		if(!d[i])d[i]=p[++t]=i,mu[i]=md-1;
		rep(j,1,t){
			if(p[j]>d[i]||p[j]>m/i)break;
			d[p[j]*i]=p[j];
			if(i%p[j])mu[p[j]*i]=md-mu[i];
		}
	}
	rept(i,1,n)a[i]=read();
	for(int i=a[1];i<=m;i+=a[1])f[i]=1;
	a[0]=a[1];
	rept(i,1,n){
		int c=m/a[i];
		rep(j,1,c)s[j]=f[j*a[i]];
		rep(j,1,t){
			if(p[j]>c)break;
			drep(k,c/p[j],1)add(s[k],s[k*p[j]]);
		}
		rep(j,1,c)s[j]=(ll)s[j]*mu[j]%md;
		for(int j=a[i-1];j<=m;j+=a[i-1])f[j]=0;
		rep(j,1,c)f[j*a[i]]=s[j];
		rep(j,1,t){
			if(p[j]>c)break;
			for(int k=1,k1=p[j];k1<=c;++k,k1+=p[j])add(f[k1*a[i]],f[k*a[i]]);
		}
	}
	for(int i=a[n-1];i<=m;i+=a[n-1])add(ans,f[i]);
	cout<<ans;
	return 0;
}
posted @ 2024-12-07 00:01  zifanwang  阅读(2)  评论(0编辑  收藏  举报