给出一个长度为N的数列{a[n]},1<=a[i]<=M(1<=i<=N)。 现在问题是,对于1到M的每个整数d,有多少个不同的数列b[1], b[2], …, b[N],满足: (1)1<=b[i]<=M(1<=i<=N); (2)gcd(b[1], b[2], …, b[N])=d; (3)恰好有K个位置i使得a[i]<>bi 注:gcd(x1,x2,…,xn)为x1, x2, …, xn的最大公约数。 输出答案对1,000,000,007取模的值。
我设f[i]表示d=i的答案 用一个g[i]表示所有i|d的d的答案和。 然后莫比乌斯反演一波。 所以我们只需要算g。 可以通过预处理求出不是d的倍数有t个。 那么这个t个一定不和原来相同 剩下n-t个中要有k-t个和原来相同 就是一个组合数咯
#include<cstdio>#include<algorithm>#include<cmath>#define fo(i,a,b) for(i=a;i<=b;i++)#define fd(i,a,b) for(i=a;i>=b;i--)using namespace std;typedef long long ll;const int maxn=300000+10,mo=1000000007;int mu[maxn],cnt[maxn],b[maxn],PRi[maxn],f[maxn],g[maxn],fac[maxn],inv[maxn],a[maxn];bool bz[maxn];int i,j,k,l,t,n,m,top;int quicksortmi(int x,int y){ if (!y) return 1; int t=quicksortmi(x,y/2); t=(ll)t*t%mo; if (y%2) t=(ll)t*x%mo; return t;}int C(int n,int m){ if (n<m||n<0||m<0) return 0; return (ll)fac[n]*inv[m]%mo*inv[n-m]%mo;}int main(){ scanf("%d%d%d",&n,&m,&k); fo(i,1,n){ scanf("%d",&a[i]); b[a[i]]++; } fo(i,1,m) fo(j,1,m/i) cnt[i]+=b[i*j]; fac[0]=1; fo(i,1,n) fac[i]=(ll)fac[i-1]*i%mo; inv[n]=quicksortmi(fac[n],mo-2); fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%mo; top=0; mu[1]=1; fo(i,2,m){ if (!bz[i]){ pri[++top]=i; mu[i]=-1; } fo(j,1,top){ if ((ll)i*pri[j]>m) break; bz[i*pri[j]]=1; if (i%pri[j]==0){ mu[i*pri[j]]=0; break; } mu[i*pri[j]]=-mu[i]; } } fo(i,1,m){ t=n-cnt[i]; if (k<t) continue; g[i]=(ll)C(n-t,k-t)*quicksortmi(m/i-1,k-t)%mo*quicksortmi(m/i,t)%mo; } fo(i,1,m){ fo(j,1,m/i) f[i]=(f[i]+g[i*j]*mu[j])%mo; (f[i]+=mo)%=mo; printf("%d%c",f[i],i==m?'/n':' '); }}新闻热点
疑难解答