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;
}

猜你喜欢

转载自www.cnblogs.com/Acestar/p/12961992.html