P7575 「PMOI-3」公約數 題解

zifanwang發表於2024-12-07

考慮 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;
}

相關文章