bzoj2257: [Jsoi2009]瓶子和燃料

本文介绍了一种求解多个数中选取特定数量的数,使得这些数的最大公约数最小的算法。通过分解质因数并统计每个数的因子,最终找到符合条件的最小最大公约数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目
#Solution
根据裴蜀定理可得:容量为 v 1 , v 2 , . . . . . v k v1,v2,.....vk v1,v2,.....vk k k k个瓶子所能倒出的最小值为 g c d ( v 1 , v 2 , . . . , v k ) gcd(v1,v2,...,vk) gcd(v1,v2,...,vk)
其实这结论我是猜出来的,但是确实可以从充分性和必要性方面分别证明
然后问题等价为: n n n个数里选 k k k个,使它们的 g c d gcd gcd最小,可以把 n n n个数所有因子算出后排序,如果有一个因子出现次数 > = k >=k >=k,那这个因子就是可行的,从大到小枚举就可以得出最大值
#Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,k,i,j,x,tmp,p[5000002],cnt;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
int main(){
	n=read();k=read();
	for (i=0;i<n;i++){
		x=read();
		for (j=1;(ll)j*j<=x;j++)
			if (x%j==0) p[++cnt]=j,p[++cnt]=x/j;
		if ((ll)(j-1)*(j-1)==x) cnt--;
	}
	sort(p+1,p+cnt+1);
	tmp=1;
	for (i=cnt;i>=0;i--)//注意要包括0,不包括可能能A,但是2 2 4 9就会WA
		if (p[i]==p[i+1]) tmp++;
		else{
			if (tmp>=k){
				printf("%d",p[i+1]);
				return 0;
			}
			tmp=1;
		}
}

下面这个洛谷rank2,bzoj前20页都没有。。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000007;
int n,k,i,j,x,tmp,p[N],cnt[N],t,ans;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
inline int ha(int x){
	int v=x%N;
	for (;p[v] && p[v]!=x;v=v==N-1?0:v+1);
	return v;
}
int main(){
	n=read();k=read();
	for (i=0;i<n;i++){
		x=read();
		for (j=1;(ll)j*j<=x;j++)
			if (x%j==0){
				t=ha(j);
				cnt[t]++;
				p[t]=j;
				if (j!=x/j){
					t=ha(x/j);
					cnt[t]++;
					p[t]=x/j;
				}
			}
	}
	for (i=0;i<N;i++)
		if (cnt[i]>=k && p[i]>ans) ans=p[i];
	printf("%d",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值