【bzoj2257】[Jsoi2009]瓶子和燃料 扩展裴蜀定理+STL-map

题目描述

给出 $n$ 个瓶子和无限的水,每个瓶子有一定的容量。每次你可以将一个瓶子装满水,或将A瓶子内的水倒入B瓶子中直到A倒空或B倒满。从中选出 $k$ 个瓶子,使得能够通过这 $k$ 个瓶子凑出的最小体积最大。求这个体积。

输入

第1行:2个整数N,K,  
第2..N 行:每行1个整数,第i+1 行的整数为Vi  

输出

仅1行,一个整数,表示火星人给出燃料的最大值。

样例输入

3 2 


4

样例输出

4


题解

扩展裴蜀定理+STL-map

显然通过容量为 $v_1,v_2,...,v_k$ 的瓶子能够凑出的容量 $c$ 满足:$v_1x_1+v_2x_2+...+v_kx_k=c$ 存在整数解。

根据裴蜀定理有 $\gcd(v_1,v_2,...,v_k)|c$ 。因此最小正整数就是它们的 $\gcd$ 。

原问题转化为:从 $n$ 个数中选出 $k$ 个,使得它们的 $\gcd$ 最大。

枚举所有数的所有约数,判断其是否是至少 $k$ 个数的约数,并更新答案即可。这个过程可以使用STL-map维护。

时间复杂度 $O(n\sqrt v+n\log n·约数个数)$ ,由于约数个数极少,远达不到 $\sqrt v$ ,因此可以通过。

#include <map>
#include <cstdio>
using namespace std;
map<int , int> mp;
map<int , int>::iterator it;
int main()
{
	int n , k , i , j , x , ans = 0;
	scanf("%d%d" , &n , &k);
	for(i = 1 ; i <= n ; i ++ )
	{
		scanf("%d" , &x);
		for(j = 1 ; j * j <= x ; j ++ )
		{
			if(x % j == 0)
			{
				mp[j] ++ ;
				if(j * j != x) mp[x / j] ++ ;
			}
		}
	}
	for(it = mp.begin() ; it != mp.end() ; it ++ )
		if(it->second >= k)
			ans = max(ans , it->first);
	printf("%d\n" , ans);
	return 0;
}

 

posted @ 2018-03-20 16:55  GXZlegend  阅读(425)  评论(0编辑  收藏  举报