zoukankan      html  css  js  c++  java
  • 打怪(CDQ分治+斜率优化)

    题目描述

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

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

    求所受伤害最小值。

    解法:

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

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

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

    [c_i*a_j>c_j*a_i ]

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

    [e_i=a_isum_{j=1}^ {i-1}c_j+a_i(c_i-1)+c_isum_{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;
    }
    
    
  • 相关阅读:
    SQL Server 2005中的分区表(一):什么是分区表?为什么要用分区表?如何创建分区表?
    SQL Server 分区表
    SQL Server表分区
    Red Gate系列之一 SQL Compare 10.4.8.87 Edition 数据库比较工具 完全破解+使用教程
    C# WinForm开发系列
    C# WinForm开发系列
    富文本编辑器 CKeditor 配置使用
    C# ListView用法详解
    C# WinForm给Button或其它控件添加快捷键响应
    WebApi安全性 使用TOKEN+签名验证
  • 原文地址:https://www.cnblogs.com/Suiyue-Li/p/12575573.html
Copyright © 2011-2022 走看看