CF792F Mages and Monsters

本文介绍了一种基于魔法战斗的策略算法,通过构建三维坐标系和左上凸包,实现对魔法消耗、伤害输出和时间消耗的有效管理。利用set集合进行动态调整,确保在面对不同怪物时,能够快速找到最优的战斗策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
题意:有初始魔法值M,有两个操作:
①:学习一个魔法,该魔法施放时每秒消耗法力值y,造成伤害x。
②:出现一个怪物,其生命值为h,有t的时间限制将其击败。在攻击过程中,在同一时刻只能释放一个法术,一个法术可以释放任意时间。可以从已学习的任意法术中选择。询问是否可以击败。每次刚遇见怪物时法力值为满。

思维好题。我们可以建立一个立体坐标系。有伤害、法力值、时间,三维,分别令为x、y、z。于是一个法术就可以对应一条直线(tx,ty,t)。将所有法术画出来,我们可以得到一个没有底面的棱锥(内部也可能有一些线)。可行解的范围就在这个棱锥内(包含棱锥上)。我们可以用一个y=M的面去截这个棱锥。那么我们就得到了一个截面,面上有一轴伤害,一轴时间。这个截面上的一个点(a,b)就对应一种方案:法力值消耗为M,伤害为a,消耗时间为b。那么在考虑答案的时候,维护一个左上凸包。对于一个询问的时间t,只需要找到时间小于等于t的最大伤害即可。
于是考虑将坐标转化:对于一个每秒消耗法力值y,造成伤害x的法术,对应截面上(m/y,m/y*x)的一个点。左上凸包可以用一个set集合维护。按照横坐标从小到大排序。加入一个点时,往左右两边做叉积判断。判断完后,往右判断纵坐标,保证是左上凸包。总之,set写起来十分的淡疼。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int mod=1e6;
int q,op,j=0;double m,x,y,mx=0;
struct Point{
double x,y;
Point(double X=0,double Y=0){x=X,y=Y;}
    friend inline Point operator+(const Point &a,const Point &b){return Point(a.x+b.x,a.y+b.y);}
    friend inline Point operator-(const Point &a,const Point &b){return Point(a.x-b.x,a.y-b.y);}
    friend inline double cross(const Point &a,const Point &b){return a.x*b.y-a.y*b.x;}
    friend inline bool operator<(const Point &a,const Point &b){return a.x==b.x?a.y<b.y:a.x<b.x;}
    inline void print(){printf("%.2lf  %.2lf\n",x,y);}
};set<Point> G;
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x;
}
inline void add(Point x){
    set<Point>::iterator l,r,tmp,pos;
    mx=max(mx,x.y),l=r=G.upper_bound(x),--l;
    if(r==G.end()) --r;
    if((x.y<mx)&&cross(*r-x,*l-x)>=0) return;
    while(l!=G.begin()){
        tmp=l,--tmp;
        if(cross(*l-*tmp,x-*tmp)<0) break;
        G.erase(*l),l=tmp;
    }
    while(1){
        tmp=r,++tmp;
        if((tmp==G.end())||cross(*r-*tmp,x-*tmp)>0) break;
        G.erase(*r),r=tmp;
    }G.insert(x),pos=G.upper_bound(x);
    while(1){
        tmp=pos,++tmp;
        if((pos==G.end())||((*pos).y>=x.y)) break;
        G.erase(*pos),pos=tmp;
    }
}
inline bool query(Point x){
    set<Point>::iterator l,r,T;
    T=G.end(),--T;
    if(x.x>=(*T).x) return (*T).y>=x.y;
    l=r=G.upper_bound(x),--l;
    return cross(*r-x,*l-x)>=0;
}
int main(){
    //freopen("CF792F.in","r",stdin);
    q=read();scanf("%lf",&m);G.insert(Point(0,0));
    for(int i=1;i<=q;++i){
        op=read(),x=(read()+j)%mod+1,y=(read()+j)%mod+1;
        if(op==1) add(Point(m/y,m*x/y));
        if(op==2){
            bool flag=query(Point(x,y));
            if(flag) puts("YES"),j=i;
            else puts("NO");
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值