BZOJ1816 CQOI2010 扑克牌 贪心

题目传送门:https://www.lydsy.com/JudgeOnline/problem.php?id=1816

题意:有$N$堆牌,第$i$堆牌有$c_i$张牌,还有$M$张$joker$,每一次可以从$N$堆牌中任选一张组成一副牌,或者在任意$N - 1$堆中选择$1$张牌加上一张$joker$组成一副牌,问最多能组成多少副牌。$N \leq 50 , m , c_i \leq 5 \times 10^8$


清流题qwq

每一次的消除一定是在牌最少的一堆使用$joker$,$joker$就等效于这一堆中额外的一张牌。所以先考虑加完$joker$一起减的情况,也就是先把第一少的堆加$joker$直至与第二少的堆牌量相同,然后在前两少的堆中同时加$joker$直至前三堆牌量相同,这么一直做下去,直到$joker$不够或者$N$堆加成一样高,把剩下的$joker$平摊在每一堆上就是最优策略。

然而这样子会有一个问题:一次取牌只能取出一张$joker$,但我们的方案中很有可能一次取出多张$joker$。我们不妨这样考虑:在排序之后,前两堆牌每一次至少会取出一张牌,前三堆牌每一次至少会取出两张牌,以此类推,我们可以得到一个取牌的上界,将这个上界与上面的答案取$min$即可

所以请无视我的高精$QuQ$

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 
 5 const int MOD = 1e9;
 6 inline ll read(){
 7     ll a = 0;
 8     char c = getchar();
 9     while(!isdigit(c))
10         c = getchar();
11     while(isdigit(c)){
12         a = (a << 3) + (a << 1) + (c ^ '0');
13         c = getchar();
14     }
15     return a;
16 }
17 
18 struct Bignum{
19     int num[3];
20     Bignum(ll x){
21         int i = 0;
22         while(i <= 2){
23             num[i++] = x % MOD;
24             x /= MOD;
25         }
26     }
27     Bignum operator +=(ll x){
28         int i = 0;
29         while(x){
30             num[i] += x % MOD;
31             if(num[i] > MOD){
32                 num[i + 1]++;
33                 num[i] -= MOD;
34             }
35             x /= MOD;
36             i++;
37         }
38         return *this;
39     }
40     
41     ll operator /(int x){
42         ll sum = 0 , ans = 0;
43         for(int i = 2 ; i >= 0 ; i--){
44             sum = sum * MOD + num[i];
45             ans = ans * MOD + sum / x;
46             sum %= x;
47         }
48         return ans;
49     }
50 };
51 ll num[51];
52 
53 int main(){
54     freopen("easy.in" , "r" , stdin);
55     freopen("easy.out" , "w" , stdout);
56     int N;
57     ll M;
58     N = read();
59     M = read();
60     for(int i = 1 ; i <= N ; i++)
61         num[i] = read();
62     sort(num + 1 , num + N + 1);
63     int dir = 1;
64     ll minN = 1e18 + 1;
65     Bignum sum = num[dir];
66     while(dir < N && num[dir + 1] - num[dir] <= M / dir){
67         M -= (num[dir + 1] - num[dir]) * dir;
68         sum += num[dir + 1];
69         minN = min(minN , sum / dir);
70         dir++;
71     }
72     cout << min(minN , num[dir] + M / dir) << endl;
73     return 0;
74 }

 

转载于:https://www.cnblogs.com/Itst/p/9755197.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值