题解 P5068【[Ynoi2015] 我回来了】
题解
提供一种比较好写的在线做法。
考虑对于任意的
因此,我们只需要维护出,当增加一个随从时,有哪些位置的
设
开一棵线段树,将每个 vector<int>,记录这里的区间的下标。对于新加入的血量为
在线段树上如何更新一个
有一个实现细节:这样做可能导致线段树的 vector 内有一些本应被删除、但未被删除的元素,只需在下一次遍历时,检查
时间复杂度
代码
只写了 1.5k。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
#define Debug(...) fprintf(stderr,__VA_ARGS__)
using ll=long long;
const int N=1e5+5;
int n,m,L[N],R[N];
struct BIT{
int t[N];
void Add(int p,int k){for(;p<=n;p+=p&-p) t[p]+=k;}
int Query(int p){int res=0;for(;p;p-=p&-p) res+=t[p];return res;}
}bit,exist;
struct SegTree{
vector<int> s[N*4];
void Insert(int p,int id,int l,int r){
if(L[id]<=l&&r<=R[id]){s[p].push_back(id);return;}
int mid=(l+r)/2;
if(L[id]<=mid) Insert(p*2,id,l,mid);
if(R[id]>mid) Insert(p*2+1,id,mid+1,r);
}
void Oper(int p,int pos,int l,int r){
for(int x:s[p]){
if(l>R[x]||r<L[x]) continue;
int tot=0;
while(L[x]<=n){
if(exist.Query(R[x])-exist.Query(L[x]-1)==0) break;
L[x]=min(n+1,L[x]+x),R[x]=min(n,R[x]+x);
++tot;
}
bit.Add(x,tot);
if(L[x]<=n) Insert(1,x,1,n);
}
s[p].clear();
if(l==r) return;
int mid=(l+r)/2;
if(pos<=mid) Oper(p*2,pos,l,mid);
else Oper(p*2+1,pos,mid+1,r);
}
}seg;
int main(){
#ifndef zyz
ios::sync_with_stdio(false),cin.tie(nullptr);
#endif
cin>>n>>m;
For(i,1,n) L[i]=1,R[i]=i,seg.Insert(1,i,1,n),bit.Add(i,1);
For(_,1,m){
int op;cin>>op;
if(op==1){
int h;cin>>h;
exist.Add(h,1);
seg.Oper(1,h,1,n);
}else{
int l,r;cin>>l>>r;
cout<<bit.Query(r)-bit.Query(l-1)<<'\n';
}
}
return 0;
}