洛谷P4422 [COCI2017-2018#1] Deda
一道比较显然的线段树,因为我们要求大于等于 \(B\) 且在第 \(Y\) 站(包含第 \(Y\) 站)以前下车的最年轻的小孩是多大,所以我们可以发现把年龄作为下角标,存是在第几站下的车,每次查找年龄为 \(B\) ~ \(N\) 中的第一个在小于等于 \(Y\) 站下车的小孩的年龄。
我们用 \(mins_{rt}\) 存以 \(rt\) 为根的子树中值最小的是几,然后每次查找就先找出每个合法子树,再在这个子树中判断左子树的最小值是否合法,如果合法就进左子树,否则,进右子树,最后返回下角标,因为我们要找最小的,所以从左子树开始,之后再写一个单点修改就可以了。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define N 200010
#define INF 0x3f3f3f3f
using namespace std;
int mins[N<<2],ans;
void pushup(int rt)
{
mins[rt]=min(mins[rt<<1],mins[rt<<1|1]);
}
void update(int p,int x,int l,int r,int rt) //单点修改:mins[p]=x
{
if(l==r)
{
mins[rt]=x;
return;
}
int mid=(l+r)>>1;
if(p<=mid) update(p,x,l,mid,rt<<1);
else update(p,x,mid+1,r,rt<<1|1);
pushup(rt);
}
int Find(int x,int l,int r,int rt) //查找以rt为根的子树中的第一个<=x的下角标
{
if(mins[rt]>x) return INF;
else if(l==r) return l;
int mid=(l+r)>>1;
if(mins[rt<<1]<=x) return Find(x,l,mid,rt<<1);
else return Find(x,mid+1,r,rt<<1|1);
}
void query(int L,int R,int x,int l,int r,int rt) //在整个树中查找范围在L-R内的子树
{
if(L<=l&&r<=R)
{
ans=Find(x,l,r,rt);
return;
}
int mid=(l+r)>>1;
if(L<=mid)
{
query(L,R,x,l,mid,rt<<1);
if(ans!=INF) return;
}
if(mid<R) query(L,R,x,mid+1,r,rt<<1|1);
}
int read() //快读
{
int x=0;
char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
int main()
{
int n=read(),m=read();
memset(mins,0x7f,sizeof(mins));
while(m--)
{
char ch[5];
scanf("%s",ch);
int x=read(),y=read();
if(ch[0]=='M')
update(y,x,1,n,1); //年龄为y的在第x站下车
else
{
query(y,n,x,1,n,1); //在y-n岁中找第一个<=x的年龄
if(ans==INF) puts("-1");
else printf("%d\n",ans);
}
}
return 0;
}
$$A\ drop\ of\ tear\ blurs\ memories\ of\ the\ past.$$