CF1043F Make It One
题目传送门:CF1043F
洛谷入口
题目大意:
原题翻译:\(Shirley有一个数列{a_i}^n_{i-1} ,她可以选出这些数中的任意多个,然后得到等于这些数最大公因数的分数\)
\(现在,她想要在得到1分的前提下,使选择的数尽可能少,那么,她应该选择多少个数呢\)
\(如果任意选择都不能得到1分,请输出−1\)
\(简化题目:从一堆数中选择最少的数,使它们的gcd=1\)
数据范围
\(\circ\) \(1\le n\le3\times10^5\)
\(\circ\) \(1\le a_i\le3\times10^5\)
题解
首先,题意是要找最少的个数使得他们值的gcd=1
那么个数不超过7个
因为\(2\times3\times5\times7\times11\times13\times17>3\times10^5\)
既然这样,枚举个数时间就影响不大了
接下来,对于i个数,穷举情况,惨烈挂掉
那么,其实上述拆开来就是问
选i个时有没有使gcd=1的情况
gcd肯定是被一步步筛小的
那可以到达某个小的gcd的可能情况也会越来越少……
等等!不就是方案数吗?不如来计数地DP?
Bingo√
状态如所言,g_{i,j}表示用i个数,做到其i个都有j这个因数的方案数
那么其实g_{i,j}很好求
就先预处理出来cnt_i
表示有i这个因数的有多少个
那\(g_{i,j}=C^i_{cnt_j}=\frac{A^i_{cnt_j}}{i!}=\frac{cnt_j!}{(cnt_j-i)!\times i!}\)
所以这里用到了求阶乘及其逆元
而f_{i,j}要复杂些,但其实思考一下其实本蒟蒻不止一下
哪些不可以DP下去呢?就会发现
要除去的是那些gcd不止包含j,还有其他的那些,即
\(\sum\limits^{x\times j\le (a_i)_{max}}_{x=2}\)
那么只要倒着枚举j即可求出全部的f_{i,j}了
再看所有的f_{i,1}中
找一个有值的且i是其中最小的
即为答案了
对了还有就是因为阶乘及其逆元的调用很频繁
所以建议预处理好
好了其他细节不说了
此题完结!!!下见代码↓↓↓
AC代码
#include<bits/stdc++.h>//不要学我,我懒
#define ll long long
using namespace std;
ll inv[300010],mod=1e9+7,n,a[300010],maxn;
ll bm[300010],dp[300010],cnt[300010];
ll ksm(ll a,ll b){
ll tot=a,tmp=1;
while(b){
if(b&1)tmp=tmp*tot%mod;
tot=tot*tot%mod;
b>>=1;
}
return tmp;
}
void getInv(ll x){
bm[0]=1;
for(ll i=1;i<=x;i++)bm[i]=bm[i-1]*i%mod;//求1!,2!,3!,……n!
inv[x]=ksm(bm[x],mod-2);//求n!的逆元之费马小定理
for(ll i=x-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;//求1!,2!,3!,……(n-1)!的逆元
}
int main(){
scanf("%d",&n);
getInv(n);
for(ll i=1;i<=n;i++){
scanf("%d",&a[i]);
cnt[a[i]]++;
maxn=maxn>a[i]?maxn:a[i];
}
for(ll i=1;i<=maxn;i++)for(ll j=2;i*j<=maxn;j++)cnt[i]+=cnt[i*j];
for(ll i=1;i<=7;i++){
for(ll j=1;j<=maxn;j++)dp[j]=0;
for(ll j=maxn;j>=1;j--){
if(cnt[j]<i)continue;
dp[j]=bm[cnt[j]]*inv[cnt[j]-i]%mod;//A_cnt[j]^i
dp[j]=dp[j]*inv[i]%mod;//C_cnt[j]^i
for(ll k=2;k*j<=maxn;k++){
dp[j]=(dp[j]-dp[j*k]+mod)%mod;
}
}
if(dp[1]){
printf("%d",i);
goto brown;
}
}
printf("-1");
brown:return 0;
}