P2471 [SCOI2007]降雨量(线段树)

题目描述

我们常常会说这样的话:“X 年是自 Y 年以来降雨量最多的”。它的含义是 X 年的降雨量不超过 Y 年,且对于任意 Y<Z<X,Z 年的降雨量严格小于 X 年。例如 2002,2003,2004 和 2005 年的降雨量分别为 4920,5901,2832 和 3890,则可以说“2005 年是自 2003 年以来最多的”,但不能说“2005 年是自 2002 年以来最多的”由于有些年份的降雨量未知,有的说法是可能正确也可以不正确的。

输入格式

输入仅一行包含一个正整数 nnn,为已知的数据。以下 n 行每行两个整数 y i y_i yi r i r_i ri,为年份和降雨量,按照年份从小到大排列,即 y i < y i + 1 ​ y_i<y_{i+1}​ yi<yi+1。下一行包含一个正整数 m,为询问的次数。以下 m 行每行包含两个数 Y 和 X ,即询问“X 年是自 Y 年以来降雨量最多的。”这句话是必真、必假还是“有可能”。

样例

6
2002 4920
2003 5901
2004 2832
2005 3890
2007 5609
2008 3024
5
2002 2005
2003 2005
2002 2007
2003 2007
2005 2008

输出格式

对于每一个询问,输出true,false或者maybe
false
true
false
maybe
false

算法简述

用线段树维护年份区间的最大值,并同时记录区间的连续性,查询即可

细节

如何判断区间连续?

若连续,则左区间连续,右区间连续,且左区间右端点和右区间左端点差为1
否则不连续

线段树

排序,用下标建树

代码实现

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<climits>
#include<cctype>
#include<vector>
#include<map>
#include<queue>
#include<set>
#include<string>
#include<stack>
#include<string>
#include<string.h>
using namespace std;
#pragma GCC optimize(2)
#pragma warning(disable:4996)

using namespace std;
const int maxn = 60000;
struct node
{
	int y, v;
}a[maxn];
struct ans
{
	int flg, v;//flg标记这个节点维护的区间是否连续,v表示这个区间的最大降水量
};
ans maxr[maxn << 2];
int n;
void build(int x, int l, int r)
{
	if (l == r)
	{
		maxr[x] = { 1,a[l].v };
		return;
	}
	int mid = l + (r - l) / 2;
	build(x << 1, l, mid);
	build(x << 1 | 1, mid + 1, r);
	maxr[x].v = max(maxr[x << 1].v, maxr[x << 1 | 1].v);
	if (a[mid + 1].y - a[mid].y == 1 && maxr[x << 1].flg && maxr[x << 1 | 1].flg)//如果左右节点区间连续,且左区间连续右区间也连续,x节点维护的区间就是连续的
	{
		maxr[x].flg = 1;
	}
	else
	{
		maxr[x].flg = 0;
	}

}
ans query(int x, int l, int r, int ql, int qr)//询问y[ql]到y[qr]年的最大值
{
	if (l >= ql && r <= qr)
	{
		return maxr[x];
	}
	int mid = (l + r) >> 1;
	ans res, tmp;
	res.flg = 1;
	res.v = INT_MIN;
	bool f1=0, f2=0;
	if (ql <= mid)
	{
		f1 = 1;
		tmp = query(x << 1, l, mid, ql, qr);
		res.v = max(res.v, tmp.v);
		if (!tmp.flg)//如果左右有一部分不连续,那么这个返回指就不连续
		{
			res.flg = 0;
		}
	}
	if (qr > mid)
	{
		f2 = 1;
		tmp = query(x << 1 | 1, mid + 1, r, ql, qr);
		res.v = max(res.v, tmp.v);
		if (!tmp.flg)
		{
			res.flg = 0;
		}
	}
	if (f1 && f2)
	{
		if (a[mid + 1].y - a[mid].y != 1)
		{
			res.flg = 0;
		}
	}
	return res;
}
inline int search_upper(int x)//搜索大于等于x年的下标
{
	int l = 1, r = n, mid, res = INT_MIN;
	while (l <= r)
	{
		mid = (l + r) >> 1;
		if (a[mid].y == x)
		{
			return mid;
		}
		if (a[mid].y > x)
		{
			res = mid;
			r = mid - 1;
		}
		else
		{
			l = mid + 1;
		}
	}
	return res;
}
inline int search_lower(int x)//搜索一个小于等于x年的下标
{
	int l = 1, r = n, mid, res = INT_MIN;
	while (l <= r)
	{
		mid = (l + r) >> 1;
		if (a[mid].y == x)
		{
			return mid;
		}
		else if (a[mid].y < x)
		{
			res = mid;
			l = mid + 1;
		}
		else
		{
			r = mid - 1;
		}
	}
	return res;
}
int main()
{
	//freopen("data.in", "r", stdin);
	//freopen("result.out", "w", stdout);
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d%d", &a[i].y, &a[i].v);//y是年份,v是降雨量
	}
	build(1, 1, n);
	int m; scanf("%d", &m);
	while (m--)
	{
		int l, r; scanf("%d%d", &l, &r);
		if (r <= l)
		{
			cout << "false\n";
			continue;
		}
		if (l >= a[n].y || r <= a[1].y)
		{
			cout << "maybe\n";
			continue;
		}
		int L = search_upper(l);
		int R = search_lower(r);
		if (a[L].y == l && a[R].y == r)
		{
			if (a[L].v >= a[R].v)
			{
				if (R - L == 1)
				{
					if (a[R].y - a[L].y == 1) 
						cout << "true\n";
					else cout << "maybe\n";
					continue;
				}
				ans res = query(1, 1, n, L + 1, R - 1);
				if (res.v >= a[R].v)
				{
					cout << "false\n";
				}
				else
				{
					if (res.flg && a[L + 1].y == l + 1 && a[R - 1].y == r - 1)
					{
						cout << "true\n";
					}
					else
					{
						cout << "maybe\n";
					}
				}
			}
			else
			{
				cout << "false\n";
			}
		}
		else if (a[R].y == r)
		{
			if (r - l == 1)
			{
				cout << "maybe\n";
				continue;
			}
			if (R == L)
			{
				cout << "maybe\n";
				continue;
			}
			int RR = search_lower(r - 1);
			if (RR < L)
			{
				cout << "maybe\n";
				continue;
			}
			ans res = query(1, 1, n, L, RR);
			if (res.v >= a[R].v)
			{
				cout << "false\n";
			}
			else
			{
				cout << "maybe\n";
			}
		}
		else if (a[L].y == l)
		{
			if (r - l == 1)
			{
				cout << "maybe\n";
				continue;
			}
			int LL = search_upper(l + 1);
			int RR = search_lower(r - 1);
			if (RR < LL)
			{
				cout << "maybe\n";
				continue;
			}
			ans res = query(1, 1, n, LL, RR);
			if (res.v >= a[L].v)
			{
				cout << "false\n";
			}
			else
			{
				cout << "maybe\n";
			}
		}
		else
		{
			cout << "maybe\n";
		}
		continue;
	}
}
//-19 -8

有用点个赞呗@_@

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值