2 1 1 1 3 1 1 4 5
2 6
题解:
题意:
有一排有魔法值的石头,你可以从任意一个石头的右边开始从右向左放一个冲击波,这个冲击波对从右到左会造成衰减的伤害,当造成的累积伤害大于石头的魔法值这个石头就给被毁了,你n个石头,给一个次数k,问你该冲击波的能量为多少才可以把这些球按照规定次数完全消灭
思路:
这题在两次比赛中都出现了。。第一次比赛,读错题意了。。比完赛补题的时候疯狂wa,然后第二次比赛惊奇的发现还居然是上一场比赛的题。。然后就冷静下来读懂了题意。。然后接下来就是疯狂的TLE了,我也用了二分去找。。。无奈无论怎么优化都还是TLE,错了8次后无奈就去找博客了。。。发现博客模拟得很巧妙。。我为什么那么弱
我就说明一下那人的思路。。。我也是看了好久才看懂的
首先我们用二分枚举所有的p,看要使用的次数和给出规定次数k的关系来找,那么这题的难点就在给出能量p,如何去模拟获得所需的次数ans
我们从右到左枚举每一个石头的位置,保存之前所有冲击波对当前石头的能量损失和,对当前石头有影响的能量波的个数,那么每一个石头所需要的冲击波个数就是:
(当前石头的魔法值-(对当前石头有影响的冲击波个数*能量p-之前所有冲击波对当前能量的损失和))/p+1,这样就可以用o(n)的方法写出来这题了。。。qaq我的方法一直TLE
下面的代码基本就是照着博客打的。。只是加上了我的注释,因为我根本想不到这种模拟方法
希望我的注释能帮助你们理解这题。。因为我真的看了很久才懂
原博客:http://www.acmerblog.com/hdu-3717-rescue-6697.html
代码:
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<math.h>
#include<string>
#include<stdio.h>
#include<queue>
#include<stack>
#include<map>
#include<vector>
#include<deque>
#include<algorithm>
using namespace std;
#define INF 100861111
#define ll long long
#define eps 1e-15
ll n,k;
ll a[50005];//存石头的能量值
ll cent[50005];//存在第i个位置放了几个冲击波
bool find(ll x)
{
ll sum_2=0,sum_1=0,sum=0,ans=0;//sum2为前面的冲击波到当前石头的能量损失和,sum1为一个公式推出来的中间量用于更新sum2
int j=n-1;//sum为前面对该石头有影响的冲击波的个数,ans存要用的冲击波数目,j为冲击波放的位置,i为当前小球标号
for(int i=n-1;i>=0;i--)//我说一下这个公式,就是:之前的能量损失数=冲击波数目*(当前冲击波对当前位置的能量损失)注意能量损失是一个平方公式
{//这里用了三个变量储存平方公式的三个数值就是 冲击波数目(冲击波位置-当前位置)^2拆开来了,而sum1就是那个中间量
if(j>i)
{
while((j-i)*(j-i)>=x)//处理当前冲击波的影响范围
{
sum_2-=cent[j]*(j-i-1)*(j-i-1);//公式推出的三个量
sum_1-=cent[j]*(j-i-1);//为什么是减看后面可以看明白,因为后面是叠加
sum-=cent[j];
j--;
}
}
sum_2+=2*sum_1+sum;//把公式整合算出能量损失和
sum_1+=sum;//同样中间变量也要更新,因为sum变了
ll y=a[i]-sum*x+sum_2;
if(y<0)cent[i]=0;//如果之前的冲击波已经轰没了该位置的石头
else cent[i]=y/x+1;//否则就用代码上面说的公式计算需要多少冲击波
sum+=cent[i];//更新冲击波数目,注意这里是叠加了,所以上面才能减去来算出当前冲击波的数目
ans+=cent[i];//累加结果
}
return ans<=k;
}
int main()
{
ll l,r,mid;
int test,i;
scanf("%d",&test);
while(test--)
{
scanf("%lld%lld",&n,&k);
for(i=0;i<n;i++)
{
scanf("%lld",&a[i]);
cent[i]=0;
}
l=1,r=1e10;
while(l<r)//二分答案
{
mid=(l+r)/2;
if(find(mid))
{
r=mid;
}
else
l=mid+1;
}
printf("%lld\n",l);
}
return 0;
}