「TOCO Round 1」 自适应 PVZ

题意

\(n\) 个豌豆射手,\(m\) 个僵尸。

对于第 \(i\) 个僵尸,如果任意一个豌豆射手在 \(l_i\sim r_i\) 的时间里持续攻击它,僵尸 \(i\) 就会被杀死。

每一个豌豆射手在同一时间只能攻击一个僵尸,求最少无法杀死多少僵尸。

分析

首先有一个很显然的贪心,就是优先攻击 \(r\) 较小的僵尸,因为可以给后面的僵尸预留时间。

给所有僵尸按照左右端点排序,现在会有几种情况:

  1. 当前有空余的豌豆射手。直接放一个攻击就可以了。
  2. 没有空余的豌豆射手。那么给答案加 \(1\)

但是这么做会有一个漏洞,因为我们以左端点为第一关键字排序,那么后面来的僵尸可能右端点要小一些,当前的僵尸和后来的僵尸都无法杀死,那么就应该选择一个右端点小的攻击,理由和贪心策略相同。

这样我们只需要维护当前正在被攻击的僵尸的 \(r\) 即可。

这个数据结构需要支持查询全局最大值(如上)、全局最小值(判断是否有空余);进行单点插入、删除,很明显就是平衡树了。

我用的 Splay,STL 的 set 应该也可以过。(吐槽一句:平衡树板子不是蓝吗?虽然它可以用 set,但是不至于是黄吧!)

总时间复杂度 \(O(m\log n)\)

Code

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
#define dbg(x) cout<<#x<<": "<<x<<"\n"
inline ll read(){ll x=0,f=1;char c=getchar();while(c<48||c>57){if(c==45)f=0;c=getchar();}while(c>47&&c<58)x=(x<<3)+(x<<1)+(c^48),c=getchar();return f?x:-x;}
const ll mod=1e9+7,maxn=2e5+5,maxt=505;
ll n,m;
struct node{
    ll l,r;
}zom[maxn];
template<typename tp=int,int N=maxn>class Splay{
private:
    int rt,node_cnt,INF=INT_MAX;
    struct node{
        tp val;
        int cnt,siz,pre,ch[2];
    };
    vector<node>t;
    inline void pushup(int x){
        t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+t[x].cnt;
    }
    inline void rotate(int x){
        int y=t[x].pre,z=t[y].pre,k=(t[y].ch[1]==x);
        t[z].ch[t[z].ch[1]==y]=x,t[x].pre=z;
        t[y].ch[k]=t[x].ch[k^1],t[t[x].ch[k^1]].pre=y;
        t[x].ch[k^1]=y,t[y].pre=x;
        pushup(y),pushup(x);
    }
    inline void splay(int x,int to){
        while(t[x].pre!=to){
            int y=t[x].pre,z=t[y].pre;
            if(z!=to){
                ((t[z].ch[0]==y)^(t[y].ch[0]==x))?rotate(x):rotate(y);
            }
            rotate(x);
        }
        if(!to)rt=x;
    }
    inline void find(int x){
        int cur=rt;
        if(!cur)return;
        while(t[cur].ch[x>t[cur].val]&&x!=t[cur].val){
            cur=t[cur].ch[x>t[cur].val];
        }
        splay(cur,0);
    }
public:
    Splay(){
        rt=node_cnt=0;
        t.resize(N);
        t[0].siz=t[0].cnt=t[0].pre=0;
        insert(INF),insert(-INF);
    }
    inline void reserve(int cap){
        if(t.capacity()<cap)t.reserve(cap);
    }
    inline void insert(tp x){
        int cur=rt,frm=0;
        while(cur&&x!=t[cur].val){
            frm=cur,cur=t[cur].ch[x>t[cur].val];
        }
        if(cur)++t[cur].cnt;
        else{
            cur=++node_cnt;
            if(!frm)rt=cur;
            else t[frm].ch[x>t[frm].val]=cur;
            t[cur].val=x,t[cur].cnt=1,t[cur].pre=frm;
            t[cur].siz=1,t[cur].ch[0]=t[cur].ch[1]=0;
        }
        splay(cur,0);
    }
    inline int get_pre(tp x){
        find(x);
        if(t[rt].val<x)return rt;
        int cur=t[rt].ch[0];
        while(t[cur].ch[1])cur=t[cur].ch[1];
        splay(cur,0);
        return cur;
    }
    inline int get_nxt(tp x){
        find(x);
        if(t[rt].val>x)return rt;
        int cur=t[rt].ch[1];
        while(t[cur].ch[0])cur=t[cur].ch[0];
        splay(cur,0);
        return cur;
    }
    inline tp get_pre_val(tp x){return t[get_pre(x)].val;}
    inline tp get_nxt_val(tp x){return t[get_nxt(x)].val;}
    inline void erase(tp x){
        int x_pre=get_pre(x),x_nxt=get_nxt(x);
        splay(x_pre,0),splay(x_nxt,x_pre);
        int cur=t[x_nxt].ch[0];
        if(t[cur].cnt>1)--t[cur].cnt,splay(cur,0);
        else t[x_nxt].ch[0]=0;
    }
    inline tp kth(int rank){
        ++rank;
        int cur=rt,son;
        if(t[cur].siz<rank)return INF;
        while(1){
            son=t[cur].ch[0];
            if(rank>t[son].siz+t[cur].cnt){
                rank-=t[son].siz+t[cur].cnt;
                cur=t[cur].ch[1];
            }
            else if(t[son].siz>=rank)cur=son;
            else{
                splay(cur,0);
                return t[cur].val;
            }
        }
    }
    inline int get_rank(tp x){
        find(x);
        if(t[rt].val>=x)return t[t[rt].ch[0]].siz;
        else return t[t[rt].ch[0]].siz+t[rt].cnt;
    }
};
Splay<ll>splay;
inline void solve(){
    n=read(),m=read();
    for(ll i=1;i<=n;++i)zom[i].l=read(),zom[i].r=read();
    sort(zom+1,zom+n+1,[](node a,node b){
        return a.l!=b.l?a.l<b.l:a.r<b.r;
    });
    ll ans=0;
    for(ll i=1;i<=n;++i){
        ll fir=splay.get_nxt_val(-INT_MAX),sec=splay.get_pre_val(INT_MAX);
        if(m){
            --m;
            splay.insert(zom[i].r);
        }
        else if(fir<=zom[i].l){
            splay.erase(fir);
            splay.insert(zom[i].r);
        }
        else{
            ++ans;
            if(sec>zom[i].r){
                splay.erase(sec);
                splay.insert(zom[i].r);
            }
        }
    }
    printf("%lld",ans);
}
signed main(){
    ll t=1;
    srand(time(0));
    while(t--){
        solve();
    }
    return 0;
}
posted @ 2024-12-20 15:17  run-away  阅读(15)  评论(0)    收藏  举报