[洛谷P1485] 火枪打怪

前言

翻远古时期的做题记录发现这道题没过,结果现在20分钟没切掉,真是丢人。

甚至还用了对拍。

题目

洛谷

讲解

首先你不要像我一样把题看错了,溅射伤害只有一边而非两边。

显然答案具有单调性,我们直接二分,考虑实现 \(O(n)\) 或者 \(O(n\log_2n)\)\(\tt check\) 函数。

\(p-(i-j)^2\) 拆成 \(p-i^2+2ij-j^2\) 就好了,然后从右往左贪心即可。

可以用树状数组或者差分维护溅射伤害,一个带 \(\log_2n\) 一个不带。

但是我不知道带的做法能不能过。

注意二分答案时上界的选择。

时间复杂度 \(O(n\log_2n)\)

代码

差分
//12252024832524
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 500005;
int n,k;
LL a[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Min(T x){return x < 0 ? -x : x;}

LL c[MAXN][3];//i^2 i cnt
bool check(LL x)
{
	LL far = floor(sqrt(x)),lk = k;
	for(int i = 1;i <= n;++ i) 
		for(int j = 0;j < 3;++ j)
			c[i][j] = 0;
	for(int i = n;i >= 1;-- i)
	{
		for(int j = 0;j < 3;++ j) c[i][j] += c[i+1][j];
		LL hp = a[i];
		hp -= x*c[i][2]-c[i][0]+2*i*c[i][1]-c[i][2]*i*i;
		if(hp < 0) continue;
		LL nd = hp/x+1; lk -= nd;
		if(lk < 0) return 0;
		c[i][0] += nd*i*i;
		c[i][1] += nd*i;
		c[i][2] += nd;
		if(i-far-1 >= 1)
		{
			c[i-far-1][0] -= nd*i*i;
			c[i-far-1][1] -= nd*i;
			c[i-far-1][2] -= nd;
		}
	}
	return 1;
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n = Read(); k = Read();
	for(int i = 1;i <= n;++ i) a[i] = Read();
	LL l = 1,r = 1e12,ans = 1e12;
	while(l <= r)
	{
		LL mid = (l+r) >> 1;
		if(check(mid)) ans = mid,r = mid-1;
		else l = mid+1;
	}
	Put(ans,'\n');
	return 0;
}

后记

这题紫色是不是过分了?

posted @ 2021-10-04 16:08  皮皮刘  阅读(50)  评论(0编辑  收藏  举报