hdu 3717 Rescue 二分加队列优化(技巧)

Rescue

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 814    Accepted Submission(s): 198


Problem Description
The princess is trapped in a magic place. In this place, there are N magic stones. In order to rescue the princess, you should destroy all the stones. The N stones are in a straight line. We number them as s1, s2, ... sn from left to right. Each stone has a magic strength m 1, m 2, ... m n. You have a powerful skill that can do some damage to the stones. To release the skill, you should stand to the right of some stone (s i). Then you throw a power ball towards left. Initially, this ball has a power of p. When it hits a stone, it will do some damage to the stone and its power will be decreased, and the ball will continue to fly left to the next stone if its power is still positive. Formally, if you stand to the right of s i and the power ball's initial power is p, then the ball will do Max(0, p - (i - j) * (i - j)) damage to sj, for each j <= i. So from this formula, we can see that the damage to stone sj is only determined by the initial power of the ball and the number of stones between s i and s j. A stone is destroyed if the damage you do is larger than its magic strength. Note that even if a stone is destroyed, it will not disappear; your magic ball will do damage to it and the power will be decreased by that stone. You are not strong enough so that you can release at most k magic balls. It will cost a lot of energy if the power of the magic ball is too high. So what is the minimum value of p with which you can destroy all the magic stones, with no more than k magic balls? You can choose where to release each magic ball as your will, and the power of the ball must be a positive integer.
 

Input
The first line is the number of cases T (T ≤ 100). For each case, the first line gives two integers n, k (1 ≤ n ≤ 50000, 1 ≤ k ≤ 100000). The second line are n integers, giving m 1, m 2, ... m n (1 ≤ m  ≤ 10 9).
 

Output
Print minimum possible p in a line.
 

Sample Input
  
  
2 1 1 1 3 1 1 4 5
 

Sample Output
  
  
2 6
 

题意:告诉n个石头,然后现在要用至多k个魔法球把石头全部消灭,问每个魔法球的魔力值至少要是多少。每次在消灭石头的时候,可以选择站在一个石头的右侧,然后向左边扔一个魔法球,比如站在第i个石头右侧,那么对于第j个石头,会带来max( 0,p-(i-j)*(i-j) )点伤害,只有当一个石头的伤害值小于0的时候它才算完全被消灭。消灭后它不会消失,还会在那个位置

思路:要求最小值,那么直接二分一个魔法值,然后去判断一下能否满足条件。可以在任意位置扔,可以扔任意多个(总和不超过k)

有一点要知道的是,从右向左,魔力值每次减少0  1  3  5  7  9 。。。。


dis表示对于当前第I个球来说,对他能造成影响的所有球与他的距离的和,ff则表示能造成影响的球的个数。比如说,当前有ff=3个球会对造成影响,这些球和他的距离分别是p,q,k,于是累计影响就是 (x-p^2)+(x-q^2)+(x-k^2) 那么相对于上一次(x-(p-1)^2)+ (x-(q-1)^2)+(x-(k-1)^2)这一次增加的伤害为两个式子相减得 2*(p+q+k)+3 即 2*dis+ff;

每一次攻击之后总距离dis就会增加ff,因为下一个点距离每一个球距离都会增加1,所以总共距离增加ff。


</pre><pre>
#include <iostream>
#include <stdio.h>
#include <string>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>

using namespace std;
typedef long long ll;
#define INF (1ll<<60)

int n,k;
int a[55555];
queue<int>q;

int fun(ll x)
{
    ll dis=0;
    ll sum=0;//sum每次记录到达i位置时前面所有球去掉损失后的魔力值总和
    ll ff=0;
    int ans=0;//ans用来记录总共扔出的球的个数

    while(!q.empty()) q.pop();

    for(int i=n;i>=1;i--)
    {
        while(!q.empty())
        {
            ll tmp=q.front();
            if((tmp-i)*(tmp-i)<=x) break;
           //目前队列头的球已经不能对位置i的石头造成影响了,所以要删除掉,并且去除掉他对之后球的影响。
            sum-=x-(tmp-i-1)*(tmp-i-1);//因为影响都是累计出现,去掉sum中在i+1位置这个球的影响,这样对于第i个石头来说,tmp位置的球对它就没有影响了
            dis-=(tmp-i-1);
            ff--;
            q.pop();
        }
        sum-=2*dis+ff;
        dis+=ff;

        while(sum<a[i]+1)
        {
            if(ans>=k) return 0;
            ans++;
            q.push(i);
            sum+=x;
            ff++;
        }
    }
    return 1;
}

int main()
{
    int T;
    scanf("%d",&T);
    for(int i=1;i<=T;i++)
    {
        scanf("%d%d",&n,&k);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);

        ll le=1,ri=INF;
        ll mid;
        while(le<ri)
        {
            mid=(le+ri)/2;
            if(fun(mid)) ri=mid;
            else le=mid+1;
        }

        printf("%lld\n",ri);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值