DestinHistoire

 

BZOJ-2257 [Jsoi2009]瓶子和燃料(裴蜀定理)

题目描述

  有 \(n(1\leq n\leq 1000)\) 个瓶子,选择其中的 \(k\) 个来装水,所有的瓶子都没有刻度。第 \(i\) 个瓶子的容量为 \(a_i(1\leq a_i\leq 10^9)\),另一个人可以将某个瓶子装满水,将某个瓶子中的水全部倒掉,将水从瓶子 \(a\) 倒到瓶子 \(b\),直到瓶子 \(b\) 满或瓶子 \(a\) 空,倾倒过程中的损耗可以忽略。找到最优的瓶子组合,使水尽量多。

分析

  只考虑两个瓶子,假设瓶子 \(a=3\),瓶子 \(b=5\),第一步把瓶 \(a\) 装满,全都倒进瓶子 \(b\),此时 \(a=0,b=3\);第二步把瓶子 \(a\) 装满,再把 \(a\) 中的水倒入 \(b\),直到 \(b\) 倒满,此时 \(a=1,b=5\),把瓶子 \(b\) 的水全部倒掉,最小值为 \(1\),即 \(2\times 3-5=1\),这恰好是扩展欧几里得的计算过程,根据裴蜀定理,把瓶子数量推广到 \(n\) 个,则最小值为 \(\gcd(a_1,a_2,\cdots,a_n)\)

  因此题目即为从 \(n\) 个数中选择 \(k\) 个,使它们的 \(\gcd\) 最大,分解每个数字并记录约数,数目大于 \(k\) 的最大的约数即为答案。

代码

#include<bits/stdc++.h>
using namespace std;
map<int,int> mp;
int n,k,ans;
void solve(int x)
{
    for(int i=1;i*i<=x;i++)
    {
        if(x%i==0)
        {
            mp[i]++;
            if(mp[i]>=k)
                ans=max(ans,i);
            if(i*i!=x)
            {
                mp[x/i]++;
                if(mp[x/i]>=k)
                    ans=max(ans,x/i);
            }
        }
    }
}
int main()
{
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        solve(x);
    }
    cout<<ans<<endl;
    return 0;
}

posted on 2020-11-28 15:11  DestinHistoire  阅读(75)  评论(0)    收藏  举报

导航