Codeforces 1043F Make It One 经典调和级数算法,组合数,dp

题意

给 你 n 个 数 , 取 出 一 些 数 使 这 些 数 的 最 大 公 约 数 为 1 , 求 取 出 数 字 的 最 少 个 数 , 不 能 输 出 − 1. 给你n个数,取出一些数使这些数的最大公约数为1,求取出数字的最少个数,不能输出-1. n,使1,,1.
数据范围两个 3 × 1 0 5 3\times 10^5 3×105.

秒杀

可以看到当几个质数分别为 a , b , c , d , e , f . . . . . . a,b,c,d,e,f...... a,b,c,d,e,f......时, a b c d e , a b c d f , a b c e f , a b d e f , a c d e f , b c d e f abcde,abcdf,abcef,abdef,acdef,bcdef abcde,abcdf,abcef,abdef,acdef,bcdef能使答案为6.所以由于 2 × 3 × 5 × 7 × 11 × 13 × 17 > 300000 2\times 3\times 5\times 7\times 11\times 13\times17>300000 2×3×5×7×11×13×17>300000,答案最大为7.
接下去从小到大枚举答案判断是否可行.
设当前取 i i i个数字,则令 d p [ j ] dp[j] dp[j]表示取 i i i个数字使最大公约数为 j j j的方法总数.
再令 c n t [ j ] cnt[j] cnt[j]表示 a a a数组中能被 j j j整除的数字总数,则 d p [ j ] = C c n t [ j ] i − ∑ k = j × 2 3 × 1 0 5 d p [ k ] dp[j]=C^i_{cnt[j]}-\sum_{k=j\times2}^{3\times10^5}dp[k] dp[j]=Ccnt[j]ik=j×23×105dp[k]
仔细思考一下很容易出来.
最后只要看 d p [ 1 ] dp[1] dp[1],不为 0 0 0即可说明 i i i可行,直接输出.
如果到了 7 7 7还是没有答案,直接输出 − 1 -1 1就好了.
代码如下.如果 d p [ i ] dp[i] dp[i]非常大,只要选个大质数取模即可.

#include<bits/stdc++.h> //Ithea Myse Valgulious
/*省略*/
using namespace std;
const int yuzu=3e5,mod=1e9+7;
typedef ll fuko[yuzu|10];
fuko cnt,dp,jic={
    
    1},inv={
    
    1};

ll kasumi(ll a,ll b=mod-2) {
    
    
  ll ans=1;
  for (;b;b>>=1,a=a*a%mod) if (b&1) ans=ans*a%mod;
  return ans;
}

int main() {
    
    
  int i,n=read(),j,k;
  for (i=1;i<=n;++i) ++cnt[read()];
  for (i=1;i<=yuzu;++i) jic[i]=jic[i-1]*i%mod;
  inv[yuzu]=kasumi(jic[yuzu]);
  for (i=yuzu-1;i;--i) inv[i]=inv[i+1]*(i+1)%mod;
  /*简单处理阶乘和逆元*/
  for (i=1;i<=yuzu;++i) {
    
    
    for (j=i<<1;j<=yuzu;j+=i) 
      cnt[i]+=cnt[j];
  } 
  for (i=1;i<=7;++i) {
    
    
    for (j=yuzu;j;--j) {
    
    
      dp[j]=cnt[j]<i?0:jic[cnt[j]]*inv[i]%mod*inv[cnt[j]-i]%mod;
      for (k=j<<1;k<=yuzu;k+=j) {
    
    
        dp[j]=(dp[j]-dp[k]+mod)%mod;
      }
    }
    if (dp[1]) return printf("%d\n",i)&0;
  } 
  puts("-1");
}

谢谢大家.

猜你喜欢

转载自blog.csdn.net/qq_31908675/article/details/108087984