打怪(CDQ分治+斜率优化)

题目描述

敌方有 \(n\) 只怪,每只的攻击力为 \(a_i\),血量为 \(d_i\)
我方只有一只攻击力为 \(b\) 的怪。
每回合战斗的流程为:

  • 我方选择敌方某只怪进行攻击,令其血量减少 \(b\),若此时该只怪的血量\(\leq\) 0则死亡。
  • 若敌方第 \(i\) 只怪仍然存活,则对我方造成 \(a_i\) 的伤害。
    初始时你可以选择敌方的两只怪进行秒杀。

求所受伤害最小值。

解法:

显然我方只有在连续攻击一只怪致死后,才会换下一只。设第 \(i\) 只怪需要被连续攻击:

\[c_i=\lfloor \frac{d_i-1}{b} \rfloor +1 \]

若没有秒杀的操作,只考虑我方的攻击顺序。
则第 \(i\) 只怪排在第 \(j\) 只之后,当且仅当:

\[c_i*a_j>c_j*a_i \]

于是我们按照这一关键字排序即可。
现考虑秒杀操作。若仅秒杀一只怪 ,则伤害值会减小:

\[e_i=a_i\sum_{j=1}^ {i-1}c_j+a_i(c_i-1)+c_i\sum_{j=i+1}^{n}a_j \]

考虑若确定秒杀第 \(i\) 只怪,再秒杀第 \(j\) 只怪比秒杀第 \(k\) 只怪优(其中\(j\),\(k\) < \(i\)\(c_j\)>\(c_k\)),当且仅当:

\[e_i+e_j-a_i*c_j>e_k+e_i-a_i*c_k \]

化简得:

\[a_i> \frac {e_j-e_k}{c_j-c_k} \]

这是一个斜率优化模型(不懂的话右转斜率优化),显然可以用李超树动态维护,但我不会,我们以c为横坐标,e为纵坐标的坐标轴上,有可能成为最优决策的只有上凸的点。
维护一个单调递减的上凸壳即可。

但注意到 \(a_i\)\(c_j\) 无序。
考虑用 \(CDQ\) 分治处理。
对于区间 \([l,r]\),将 \([l,mid]\)\([mid+1,r]\) 分别按 \(c\)\(a\) 排序,即可进行斜率优化。
复杂度 \(O(nlog^2n)\)
代码:

//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int N=1e6+5;
const ll mod=998244353;
const double eps=1e-5;
const double pi=acos(-1);
#define ls p<<1
#define rs p<<1|1
struct node
{
    ll x,y,z;
}a[N];
int q[N];
bool cmp(node x,node y)
{
    return x.x*y.y>x.y*y.x;
}
bool cmpy(node x,node y)
{
    return x.y<y.y;
}
bool cmpx(node x,node y)
{
    return x.x<y.x;
}
ll ans,sum;
void cdq(int l,int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    sort(a+l,a+mid+1,cmpy);sort(a+mid+1,a+r+1,cmpx);
    int s=1,e=1;q[1]=0;
    for(int i=l;i<=mid;i++)
    {
        while(s<e&&(a[q[e]].z-a[q[e-1]].z)*(a[i].y-a[q[e]].y)<=(a[i].z-a[q[e]].z)*(a[q[e]].y-a[q[e-1]].y)) e--;
        q[++e]=i;
    }
    for(int i=mid+1;i<=r;i++)
    {
        while(s<e&&(a[q[s]].z-a[q[s+1]].z)<=a[i].x*(a[q[s]].y-a[q[s+1]].y)) s++;
        ans=min(ans,sum-a[i].z-a[q[s]].z+a[i].x*a[q[s]].y);
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n,m;
    ll A=0,B=0;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i].x>>a[i].y;
        A+=a[i].x;a[i].y=(a[i].y-1)/m+1;
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        A-=a[i].x;B+=a[i].y;
        a[i].z=a[i].x*(B-1)+A*a[i].y;
        sum+=a[i].x*(B-1);
    }
    ans=sum;
    cdq(1,n);
    cout<<ans<<endl;
    return 0;
}

posted @ 2020-03-26 17:04  Suiyue_Li  阅读(267)  评论(0编辑  收藏  举报