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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章