zoukankan      html  css  js  c++  java
  • CF1043F Make It One

    题目传送门:CF1043F

    洛谷入口

    题目大意:

    原题翻译:(Shirley有一个数列{a_i}^n_{i-1} ,她可以选出这些数中的任意多个,然后得到等于这些数最大公因数的分数)
    (现在,她想要在得到1分的前提下,使选择的数尽可能少,那么,她应该选择多少个数呢)
    (如果任意选择都不能得到1分,请输出−1)

    (简化题目:从一堆数中选择最少的数,使它们的gcd=1)

    数据范围

    (circ) (1le nle3 imes10^5)
    (circ) (1le a_ile3 imes10^5)

    题解

    首先,题意是要找最少的个数使得他们值的gcd=1
    那么个数不超过7个
    因为(2 imes3 imes5 imes7 imes11 imes13 imes17>3 imes10^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)! imes i!})
    所以这里用到了求阶乘及其逆元
    而f_{i,j}要复杂些,但其实思考一下其实本蒟蒻不止一下
    哪些不可以DP下去呢?就会发现
    要除去的是那些gcd不止包含j,还有其他的那些,即

    (sumlimits^{x imes jle (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;
    } 
    

    支持一下吧,关注,点赞,评论都好!

    THE END

    Reality&Imagine
  • 相关阅读:
    C# 2008核心编程(20130713)
    java 异常处理机制
    指定节点滚动到屏幕中间的js
    mysql 数据误删恢复
    《How Tomcat works》
    HashMap中 工具方法tableSizeFor的作用
    mysql 是如何保证在高并发的情况下autoincrement关键字修饰的列不会出现重复
    为什么java io流必须得关闭
    下载文件出现内存溢出问题
    当使用junit4 对spring框架中controller/service/mapper各层进行测试时,需要添加的配置
  • 原文地址:https://www.cnblogs.com/yang-RA-NOI/p/12670526.html
Copyright © 2011-2022 走看看