hdu/3717//UVALive - 5012 Rescue 二分加思维

Rescue

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


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
 

Author
HANG, Hang
 

Source
 


二分p值,如果朴素check肯定会t,所以有一个比较巧妙的思维去优化

正常想法是从后向前扫,记录每个点已经被打的能量,这样是不好通过后面的已经确定的一些值来快速确定的

所以就记录我这个点被打的损耗的能量,也就是(j-i)*(j-i),在记录一个右边打到了这个点多少次

那么这个点已经被打的能量也就是 总次数乘以p减去损耗,设为t

设每个点原本的能量num【i】  

我在当前这个点还需要打的次数也就是   

(num【i】-t)/p+1(加一是因为题意“larger than”)

现在还需要解决的是怎么快速的通过右边的值来确定 当前这个点的 被打的损耗的能量(真的很巧妙。。。)

我们设能打到 当前点(index)右边第一个点(index+1) 的所有气功波起始点为j1,j2,j3,j4,。。jk

再设xi= ji - index -1,(i=1->k)index+1这个点的所有损耗也就是sum( xi*xi ),我们把这个值记录下来,再记录下来sum(xi),和k

再转移到index时,先把那些打不到index这个点的气功波都去掉 ,这个完全可以记录一个下标,while循环一些On就可以解决

去完之后k的值还有sum( xi*xi )sum(xi)都是有变化的,相当于是记录了能打到index这个点的气功波在对index+1这个点的时的消耗

而对index的消耗,其实只是所有xi的值都加了1

这样损耗也就是sum((xi+1)*(xi+1)) = sum(xi*xi+2*xi+1)=sum(xi*xi)+2*sum(xi)+k

之前记录的值就派上用场了


#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=50005;
ll num[maxn],cnt[maxn];
int k,n;
int check(ll x)
{
    ll sum2=0,sum1=0,sum=0,ans=0;
    int j=n-1,i;
    for(i=n-1;i>=0;i--)
    {
        if(j>i)
        {
            while((j-i)*(j-i)>=x)
            {
                sum2-=cnt[j]*(j-i-1)*(j-i-1);
                sum1-=cnt[j]*(j-i-1);
                sum-=cnt[j];
                j--;
            }
        }
        sum2+=2*sum1+sum;
        sum1+=sum;
        ll t=num[i]-sum*x+sum2;
        if(t<0)
            cnt[i]=0;
        else
            cnt[i]=t/x+1;
        ans+=cnt[i];
        sum+=cnt[i];
    }
    return ans<=k;
}
int main()
{
    int T;
    int i;
    cin>>T;
    while(T--)
    {
        cin>>n>>k;
        for(i=0;i<n;i++)
            scanf("%d",&num[i]);
        ll le=1,ri=1e12,mid,ans;
        while(le<=ri)
        {
            mid=(le+ri)>>1;
            if(check(mid))
            {
                ri=mid-1;
                ans=mid;
            }
            else
                le=mid+1;
        }
        cout<<ans<<endl;
    }
    return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值