CF725E Too Much Money

· · 题解

首先有一个性质,最优答案加且只用加一枚硬币

证明:若需要加两枚硬币 a,b ,钦定 a \ge b

共有三种情况:

$ 2. $ 若当前只用 $ b $ ,同理也不需要 $ a $ 。 $ 3. $ 若当前 $ a,b $ 均用,则到使用 $ a $ 之前,目前的值是 $ \ge a+b $ 的,且至少在使用完 $ b $ 之后,才可能被卡掉,所以对于任意一个 $ b \le x \le a $ ,在这两种情况下,选与被选的情况相同,不产生影响 。 综上,在最优答案下,我们只会恰好加入一枚硬币,否则无解 考虑枚举 $ 1 $ 到 $ c $ 之间的硬币面额,将其加入。 如何检验?我会暴力!但是 $ O(nc) $ 复杂度太炸了。 考虑记录每一个权值的个数,由枚举硬币到枚举硬币面额。 考虑 $ \sum_{1}^{k} = k \times (k+1) \div 2 $ ,所以枚举到的面额的数量为 $ \sqrt(c) $ 级别的,那么总复杂则为 $ O(c\sqrt c) $ 。 代码如下 ```cpp #include<bits/stdc++.h> using namespace std; const int N=200005; int c,n; int w[N]; set<int> s; int cnt[N]; vector<int> tmp; bool check(){ int now=c; tmp.clear(); while(now&&s.size()){ auto pre=s.upper_bound(now); if(pre==s.begin()) return true; pre--; int need=now/(*pre); now-=min(need,cnt[*pre])*(*pre); tmp.push_back(*pre); s.erase(pre); } if(now) return true; for(int i=0;i<tmp.size();i++){ s.insert(tmp[i]); } return false; } int main(){ scanf("%d%d",&c,&n); for(int i=1;i<=n;i++){ scanf("%d",&w[i]); cnt[w[i]]++; s.insert(w[i]); } for(int i=1;i<=c;i++){ s.insert(i); cnt[i]++; if(check()){ cout<<i<<endl; return 0; } cnt[i]--; if(cnt[i]==0) s.erase(s.find(i)); } cout<<"Greed is good"<<endl; return 0; } ```