codeforces #806(div4) G. Good Key, Bad Key

Problem - G - Codeforces

There are n chests. The i-th chest contains ai coins. You need to open all n chests in order from chest 1 to chest n.

There are two types of keys you can use to open a chest:

  • a good key, which costs k coins to use;
  • a bad key, which does not cost any coins, but will halve all the coins in each unopened chest, including the chest it is about to open. The halving operation will round down to the nearest integer for each chest halved. In other words using a bad key to open chest i will do ai=⌊ai/2⌋, ai+1=⌊ai+1/2⌋,…,an=⌊an/2⌋;
  • any key (both good and bad) breaks after a usage, that is, it is a one-time use.

You need to use in total n keys, one for each chest. Initially, you have no coins and no keys. If you want to use a good key, then you need to buy it.

During the process, you are allowed to go into debt; for example, if you have 1 coin, you are allowed to buy a good key worth k=3 coins, and your balance will become −2 coins.

Find the maximum number of coins you can have after opening all n chests in order from chest 1 to chest n.

Input

The first line contains a single integer tt (1≤t≤104) — the number of test cases.

The first line of each test case contains two integers n and k (1≤n≤105; 0≤k≤109) — the number of chests and the cost of a good key respectively.

The second line of each test case contains n integers ai (0≤ai≤109)  — the amount of coins in each chest.

The sum of n over all test cases does not exceed 105.

Output

For each test case output a single integer  — the maximum number of coins you can obtain after opening the chests in order from chest 1 to chest n.

Please note, that the answer for some test cases won't fit into 32-bit integer type, so you should use at least 64-bit integer type in your programming language (like long long for C++).

Example

input

5
4 5
10 10 3 1
1 2
1
3 12
10 10 29
12 51
5 74 89 45 18 69 67 67 11 96 23 59
2 57
85 60

output

11
0
13
60
58

Note

In the first test case, one possible strategy is as follows:

  • Buy a good key for 5 coins, and open chest 1, receiving 10 coins. Your current balance is 0+10−5=5 coins.
  • Buy a good key for 5 coins, and open chest 2, receiving 10 coins. Your current balance is 5+10−5=10 coins.
  • Use a bad key and open chest 3. As a result of using a bad key, the number of coins in chest 3 becomes ⌊3/2⌋=1, and the number of coins in chest 4 becomes ⌊1/2⌋=0. Your current balance is 10+1=11.
  • Use a bad key and open chest 4. As a result of using a bad key, the number of coins in chest 4 becomes ⌊0/2⌋=0. Your current balance is 11+0=11.

At the end of the process, you have 11 coins, which can be proven to be maximal.

___________________________________________________________________________

有n个箱子。第i个箱子里有ai硬币。您需要按从箱子 1 到箱子 n 的顺序打开所有 n 个箱子。

有两种类型的钥匙可以用来打开箱子:

一把好钥匙,使用需要花费k个硬币;
一把坏钥匙,它不花任何硬币,但会把每个未打开的箱子里的所有硬币减半,包括它即将打开的箱子。减半操作将向下舍入到每个切半的最接近的整数。换句话说,使用坏键打开箱子,我将做ai=⌊ai/2⌋,ai+1=⌊ai+1/2⌋,...,an=⌊an/2⌋;
任何密钥(包括好的和坏的)在使用后都会中断,也就是说,它是一次性使用的。

您需要总共使用n个钥匙,每个箱子一个。最初,你没有硬币,也没有钥匙。如果你想使用一个好的钥匙,那么你需要购买它。

在此过程中,您可以负债累累;例如,如果你有1个硬币,你可以购买一把价值k=3硬币的好钥匙,你的余额将变成-2个硬币。

找到打开所有n个箱子后(从箱子1到箱子n的顺序),你可以拥有的最大硬币数量。

请注意,某些测试用例的答案不适合 32 位整数类型,因此您应该在编程语言中使用至少 64 位整数类型(如 long long 表示 C++)。

——————————————————————————————————————————

题解:很明显这是一个贪心的题目,看到此题后最直观的想法就是看箱子里的硬币数量值不值得花费一把好钥匙,即箱子里的硬币数量-k是否大于0,但仔细去想就会发现这样做行不通,因为如果我们选择用一把坏钥匙,会导致后面箱子里的硬币数量都减半,可能会存在后面有一个放有很多枚硬币的箱子由于减半操作损失的硬币比拿好钥匙开箱更多,所以这个贪心策略显然行不通。那我们接下来去思考先用好钥匙和先用坏钥匙对硬币数量有什么影响,先好钥匙:a[i]-k+a[i+1]/2;而先钥匙:a[i]/2+a[i+1]/2-k,很明显先用坏钥匙比先用好钥匙得到的硬币数量要少,所以我们为了得到更多的硬币数量,一定要先使用好钥匙,剩下的再使用坏钥匙。

然后遍历前i个箱子使用好钥匙的情况,找到硬币数最多的那个。

这里要注意有些技巧:我们拿一个变量去存储前i个箱子使用好钥匙得到的硬币数量,每次往后多加1个箱子就在这个变量上加,这样就不会每次都要从头重复计算了。而且,由于减半操作将向下舍入到每个切半的最接近的整数,硬币最多10^9个,所以大概经历30次会变为0,此时后面的箱子都不需要再计算了。

代码如下:

#include<iostream>
using namespace std;
#define ll long long
int a[100005];
int main(){
    int t,i,j,n,k;
    cin>>t;
    while(t--){
        cin>>n>>k;
        for(i=1;i<=n;i++){
            cin>>a[i];
        }
        //sum用来存储用完好钥匙后获得的金币数
        ll ans=0,sum=0;
        //前i个箱子用好钥匙
        for(i=0;i<=n;i++){
            ll num=sum;
//因为10^9大概减半30次就变为0,我们这里写31,一定不可以写32,会导致shift too large,右移操作不要大于等于32
            for(j=i+1;j<=min(n,i+31);j++){
//每次对硬币进行减半操作一定要用一个临时变量,不然下次进循环a[j]就不是原来的了
                int tmp=a[j];
                tmp>>=(j-i);
                num+=tmp;
            }
            ans=max(ans,num);
            //第i+1个箱子用好钥匙
            if(i<n){
                sum+=a[i+1]-k;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值