没得传送门
考虑当(Atk)增大时,(Def)一定越来越没用,因为回合数在变少。所以考虑从小到大枚举(Atk)然后双指针计算。
设(f_i(x))表示在(Atk = i)时,(Def)从(x-1)到(x)时可以减少的血量的数量,易知(f_i(x) leq f_i(x - 1) , f_i(x) leq f_{i-1}(x))。对于每一个怪,当(Atk)确定之后,它的攻击回合数就确定了。对于(Atk = i),一个怪物的攻击力为(atk),攻击回合数为(T)时,相当于在函数(f_i(x))上([1,atk])区间做一个前缀加(T)的操作。当攻击回合数减小的时候也相当于前缀减然后前缀加。
不妨把这个问题变得更形象一点:有若干条线段,左端点为(1),右端点不固定,每一条线段都有一个权值。从(Def = x - 1)到(Def = x)能够减少的血量就是覆盖了(x)点的所有线段的权值和。
一件显然的事情是因为(Def)在不断变小,所以之前被当前点覆盖的线段在之后也一定被覆盖。
所以我们可以设(num_i)表示右端点为(i)的所有线段的权值之和,然后我们考虑:当当前的(Def)覆盖的线段的权值之和(leq cd)时将(Def - 1),然后把(num_{Def - 1})加入,就可以更新出新的权值了。对于删线段和加线段只需要看一下当前加入线段的右端点和(Def)的关系并进行更新就可以了。在维护攻击防御值的时候同时维护血量以保证复杂度。
那么最后的问题就是如何快速加入和删除线段,暴力做是(O(NHp))的,但是注意到(lceil frac{Hp}{i} ceil = lfloor frac{Hp - 1}{i} floor + 1)只有(sqrt{Hp})种取值,我们可以预处理出每一个转移点,就可以做到(O(sqrt{Hp}N))了
可能比较卡空间,慎用STL
然后这道题BZOJ上没有SPJ,面向数据编程可以知道:对于所有可行答案,取其中Atk最大的;对于所有Atk一样大的,取Def最大的。
#include<bits/stdc++.h>
//this code is written by Itst
using namespace std;
int read(){
int a = 0; char c = getchar();
while(!isdigit(c)) c = getchar();
while(isdigit(c)){
a = a * 10 + c - 48; c = getchar();
}
return a;
}
const int _ = 2e6 + 7;
#define PII pair < int , int >
struct node{
int nxt , rht , val;
}now[_ * 5 + 5000];
int head[_] , N , CA , CD , cnt , MXD;
long long num[_] , Ans , HP , ATK , DEF , sum , X , Y , Z;
void push(int rht , int val , int id){
now[++cnt] = (node){head[id] , rht , val};
head[id] = cnt;
}
void modify(int plc , int val){
num[plc] += val;
if(Y <= plc){sum += val; Z += (plc - Y) * val;}
}
int main(){
N = read(); CA = read(); CD = read();
for(int i = 1 ; i <= N ; ++i){
int h = read() - 1 , a = read() , d = read();
for(int j = 1 , pj ; j <= h ; j = pj + 1){
pj = h / (h / j);
if(j == 1) push(a , h + 1 , d + 1);
else push(a , h / j - h / (j - 1) , d + j);
}
push(a , -1 , h + d + 1);
MXD = max(MXD , d);
}
Ans = 1e18; Y = 1e6; Z = 1;
for(X = 1 ; X <= 2e6 ; ++X){
for(int i = head[X] ; i ; i = now[i].nxt)
modify(now[i].rht , now[i].val);
if(X <= MXD) continue;
while(Y > 1 && sum < CD){
Z += sum; sum += num[--Y];
}
if(Z + X * CA + Y * CD <= Ans){
Ans = Z + X * CA + Y * CD;
ATK = X; DEF = Y; HP = Z;
}
}
cout << HP << ' ' << ATK << ' ' << DEF;
return 0;
}