CodeForces 1321 E.World of Darkraft: Battle for Azathoth (线段树)

题意:

有n个武器,每个武器有攻击力和花费
有m个防具,每个防具有防御力和花费
有p个怪物,每个怪物有攻击力和防御力和金币
现在你必须购买武器和防具各一个,如果武器的攻击严格大于怪物,且防具的防御严格大于怪物,则可以击杀怪物
问最大收益是多少,收益等于怪物掉落金币减去武器和防具的花费

思路:

容易发现如果给武器按攻击力从小到大排序,那么枚举武器的时候,怪物击杀数量是单调的,防具同理。
把武器,怪物按攻击力排序排序,防具按防御力排序,都从小到大。
建立线段树,设a(i)为选择第i个防具的收益,初始化为防具的花费
然后枚举选择的武器,对于每个武器,击杀的怪物只会变多,
对于多出来的怪物二分出能打败这个怪物的防具区间,用线段树区间加法加上就行了,
因为要计算收益最大值,再加个一个区间最值就行了。

ps:这题用cin会tle

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
struct Node{
    int a,c;
    bool operator<(Node x){
        return a<x.a;
    }
}x[maxm],y[maxm];
struct Node2{
    int a,b,c;
    bool operator<(Node2 x){
        return a<x.a;
    }
}z[maxm];
int a[maxm<<2];
int laz[maxm<<2];
void pushup(int node){
    a[node]=max(a[node*2],a[node*2+1]);
}
void pushdown(int node){
    if(laz[node]){
        a[node*2]+=laz[node];
        a[node*2+1]+=laz[node];
        laz[node*2]+=laz[node];
        laz[node*2+1]+=laz[node];
        laz[node]=0;
    }
}
void build(int l,int r,int node){
    laz[node]=0;
    if(l==r){
        a[node]=-y[l].c;
        return ;
    }
    int mid=(l+r)/2;
    build(l,mid,node*2);
    build(mid+1,r,node*2+1);
    pushup(node);
}
void update(int st,int ed,int val,int l,int r,int node){
    if(st<=l&&ed>=r){
        laz[node]+=val;
        a[node]+=val;
        return ;
    }
    pushdown(node);
    int mid=(l+r)/2;
    if(st<=mid)update(st,ed,val,l,mid,node*2);
    if(ed>mid)update(st,ed,val,mid+1,r,node*2+1);
    pushup(node);
}
signed main(){
    int n,m,p;
    cin>>n>>m>>p;
    for(int i=1;i<=n;i++){
        scanf("%lld%lld",&x[i].a,&x[i].c);
    }
    for(int i=1;i<=m;i++){
        scanf("%lld%lld",&y[i].a,&y[i].c);
    }
    for(int i=1;i<=p;i++){
        scanf("%lld%lld%lld",&z[i].a,&z[i].b,&z[i].c);
    }
    sort(x+1,x+1+n);
    sort(y+1,y+1+m);
    sort(z+1,z+1+p);
    build(1,m,1);
    int ans=-1e18;//因为答案可能是负数,所以初始化为-inf
    int k=1;
    for(int i=1;i<=n;i++){
        while(k<=p&&x[i].a>z[k].a){//把当前武器可以击败的加进线段树
            int l=1,r=m;
            int st=-1;
            while(l<=r){//二分找出需要加的区间左端点
                int mid=(l+r)/2;
                if(z[k].b<y[mid].a){
                    st=mid;
                    r=mid-1;
                }else{
                    l=mid+1;
                }
            }
            if(st!=-1)update(st,m,z[k].c,1,m,1);
            k++;
        }
        ans=max(ans,a[1]-x[i].c);
    }
    cout<<ans<<endl;
    return 0;
}
发布了430 篇原创文章 · 获赞 36 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/104635815
今日推荐