「TOCO Round 1」 自适应 PVZ
题意
有 \(n\) 个豌豆射手,\(m\) 个僵尸。
对于第 \(i\) 个僵尸,如果任意一个豌豆射手在 \(l_i\sim r_i\) 的时间里持续攻击它,僵尸 \(i\) 就会被杀死。
每一个豌豆射手在同一时间只能攻击一个僵尸,求最少无法杀死多少僵尸。
分析
首先有一个很显然的贪心,就是优先攻击 \(r\) 较小的僵尸,因为可以给后面的僵尸预留时间。
给所有僵尸按照左右端点排序,现在会有几种情况:
- 当前有空余的豌豆射手。直接放一个攻击就可以了。
- 没有空余的豌豆射手。那么给答案加 \(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;
}

浙公网安备 33010602011771号