UVALive

来源:互联网 发布:c# python 编辑:程序博客网 时间:2024/06/08 19:56

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

 思路:

像这种题目一般想到二分,去二分魔法值,然后验证是否在k次能将其全部消灭.但是这个题难就难在怎么去check,如果是一般的验证法n^2肯定会T,所以比较难想的是怎么去check.

 这里我们要用到三个变量:

1.    sum : 前面所有的对当前石头造成的冲击波的损失能量和.(右面)

2    .len: 冲击波的长度

3     cnt: 前面有影响的冲击波次数.

num[i]数组代表要消除当前石头需要几个冲击波.

另外还需要注意的是:

比如当前石头的右侧有三个冲击波 距离他的位置分别为p,k,s,那么此位置所受的冲击波能量为:p-q*q+p-k*k+p-w*w,那么它的下一个位置所受的冲击波能量为:p-(q+1)*(q+1)+p-(k+1)*(k+1)+p-(w+1)*(w+1),二者的冲击波差为:2*dis+cnt (dis 为当前点距所有冲击波的和,cnt为冲击波的次数),也就是说每增加一个点,损失的冲击波能量就为 2*dis+cnt 

那么我们剩下要做的就是维护sum的值并记录冲击波总数去二分验证最终答案.

#include<bits/stdc++.h>#define Ri(a) scanf("%d", &a)#define Rl(a) scanf("%lld", &a)#define Rf(a) scanf("%lf", &a)#define Rs(a) scanf("%s", a)#define Pi(a) printf("%d\n", (a))#define Pf(a) printf("%lf\n", (a))#define Pl(a) printf("%lld\n", (a))#define Ps(a) printf("%s\n", (a))#define W(a) while(a--)#define CLR(a, b) memset(a, (b), sizeof(a))#define MOD 100000007#define inf 0x3f3f3f3f#define exp 0.00000001#define  pii  pair<int, int>#define  mp   make_pair#define  pb   push_backusing namespace std;typedef long long ll;const int maxn=1e5+10;ll a[maxn];ll num[maxn];int n,k;bool check(ll x){int j=n;ll ans=0;ll sum=0,cnt=0,len=0;for(int i=n;i>=1;i--){if(j>i)//j为枚举的释放冲击波的点.{while(x<=(j-i)*(j-i))//如果当前点已经不受到右端冲击波的影响.{sum-=num[j]*(j-(i+1))*(j-(i+1));//那么该点损失的能量应该减去在i+1处的那些,len-=num[j]*(j-(i+1));//距离的更新.cnt-=num[j];//冲击波次数应减少j出释放的值j--;}}sum+=2*len+cnt;//上面说过了没往左一个点损失的能量就会增加2*dis+cntlen+=cnt;//距离增加cntll y=a[i]-(x*cnt-sum);//计算当前石头是否可以被破坏if(y<0)num[i]=0;elsenum[i]=y/x+1;//如果不能被破坏必须在此点继续放冲击波.ans+=num[i];//记录最终答案.cnt+=num[i];//更新冲击波的次数.}return ans>k;}int main(){int t;Ri(t);W(t){Ri(n),Ri(k); for(int i=1;i<=n;i++)Rl(a[i]);ll l=1,r=1e12,mid,ans=-1;while(l<=r){mid=(l+r)>>1;if(check(mid))  {     l=mid+1;      }else{ r=mid-1;} }Pl(l);}return 0;}

 

1 0