题目链接:传送门
Description
我们常常会说这样的话:“
X
X
X年是自
Y
Y
Y年以来降雨量最多的”。它的含义是X年的降雨量不超过
Y
Y
Y年,且对于任意
Y
<
Z
<
X
,
Z
Y<Z<X,Z
Y<Z<X,Z年的降雨量严格小于
X
X
X年。例如
2002
,
2003
,
2004
2002,2003,2004
2002,2003,2004和
2005
2005
2005年的降雨量分别为
4920
,
5901
,
2832
4920,5901,2832
4920,5901,2832和
3890
3890
3890,
则可以说“
2005
2005
2005年是自
2003
2003
2003年以来最多的”,但不能说“
2005
2005
2005年是自
2002
2002
2002年以来最多的”由于有些年份的降雨量未
知,有的说法是可能正确也可以不正确的。
Input
输入仅一行包含一个正整数
n
n
n,为已知的数据。以下
n
n
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
m,为询问的次数。以下
m
m
m行每行包含两个数
Y
Y
Y和
X
X
X,即询问“
X
X
X年是
自
Y
Y
Y年以来降雨量最多的。”这句话是必真、必假还是“有可能”。
Output
对于每一个询问,输出true,false或者maybe。
Sample Input
6
2002 4920
2003 5901
2004 2832
2005 3890
2007 5609
2008 3024
5
2002 2005
2003 2005
2002 2007
2003 2007
2005 2008
Sample Output
false
true
false
maybe
false
HINT
100%的数据满足: 1 < = n < = 50000 , 1 < = m < = 10000 , − 1 0 9 < = y i < = 1 0 9 , 1 < = r i < = 1 0 9 1<=n<=50000, 1<=m<=10000, -10^9<=y_i<=10^9, 1<=r_i<=10^9 1<=n<=50000,1<=m<=10000,−109<=yi<=109,1<=ri<=109
Source
POJ 2637 WorstWeather Ever
-
题解:动态开点线段树或RMQ
- 考验逻辑能力好题,注意!!!心脏不好的同学不要去做这道题233
- 显然分类讨论题,讨论如下:
- 如果
X
X
X
Y
Y
Y年雨量都知道:
- 如果
X
X
X年降雨量小于等于
Y
Y
Y年降雨量,继续分类
- 如果 Y Y Y X X X 之间的所有年份降雨量数据都知道且最大降雨量 < < < X X X年的降雨量,则为 t r u e true true;
- 如果 Y Y Y X X X之间有部分年份降雨量知道且其中最大降雨量 > = X >= X >=X年降雨量,则为 f a l s e false false
- 否则为 m a y b e maybe maybe
- 否则 f a l s e false false
- 如果
X
X
X年降雨量小于等于
Y
Y
Y年降雨量,继续分类
- 如果
Y
Y
Y年降雨量知道
X
X
X年不知道
- 如果 Y Y Y X X X 之间有一些年份数据知道且最大值 > = Y >=Y >=Y年降雨量,则为 f a l s e false false
- 否则 m a y b e maybe maybe
- 如果
X
X
X年降雨量知道
Y
Y
Y年不知道
-如果 X X X Y Y Y 之间有一些年份数据知道且最大值 > = X >=X >=X年降雨量,则为 f a l s e false false- 否则 m a y b e maybe maybe
- X X X Y Y Y 都不知道 ⇒ \Rightarrow ⇒ m a y b e maybe maybe
- 如果
X
X
X
Y
Y
Y年雨量都知道:
- 显然维护区间最值然后分类大讨论就行了,首先普通线段树的话肯定是不行的, 因为这个需要 n l o g n nlogn nlogn的空间。。实际上并不是每个节点都有信息,也就是说,只有零星的最多50000个点需要维护,所以考虑没用的节点就不去申请空间了,因为每个点从根节点维护下来最多需要 log 2 50000 \log_2{50000} log250000个节点,加起来一共就是 n log n n\log n nlogn的空间,所以能满足要求。
- 我的代码其实还可以继续优化,我对所有数据建区间 [ − 1 0 9 , 1 0 9 ] [-10^9,10^9] [−109,109]的线段树,实际上可以建 [ m i n _ y e a r , m a x _ y e a r ] [min\_year,max\_year] [min_year,max_year]的就行了,然后端点查询的时候我是直接在建的树上进行单点查询,然而可以先维护一个 m a p map map,然后查询的话会使常数小一些。
- 当然这题也可以 m a p + R M Q map+RMQ map+RMQ搞搞,而且比这个好写很多,博主只是闲的蛋疼,硬生生套上一颗线段树 233 233 233
- 另外第一次被 l o n g l o n g long\ long long long卡,知道 i n t ∗ 1 L L int*1LL int∗1LL的用处了
- 两种方法对比一下:(第一个是RMQ 写的,对二个是线段树 )
-
动态开点线段树(模版)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=50000+10;
struct dynamic_segment_tree{
int tot;
struct node{
int maxx,mark;
int ls,rs,sum;
node(int a=0,int b=0,int c=0,int d=0,int e=0){
maxx=a;sum=b;mark=c;ls=d;rs=e;
}
}tree[20*maxn];
dynamic_segment_tree(){
tot=1;
memset(tree,0,sizeof(tree));
tree[0].maxx=-2e9;
}
void init(){
tot=1;
memset(tree,0,sizeof(tree));
tree[0].maxx=-2e9;
}
void pushup(int id){
tree[id].maxx=max(tree[tree[id].ls].maxx,tree[tree[id].rs].maxx);
tree[id].sum=tree[tree[id].ls].sum+tree[tree[id].rs].sum;
}
//单点更新
void update(int l,int r,int k,int val,int id){
if(l==r){
tree[id].maxx=val;
tree[id].sum=1;
return;
}
int mid=(l+r)>>1;
if(k<=mid){
if(!tree[id].ls) tree[id].ls=++tot;
update(l,mid,k,val,tree[id].ls);
}else{
if(!tree[id].rs) tree[id].rs=++tot;
update(mid+1,r,k,val,tree[id].rs);
}
pushup(id);
}
int query(int l,int r,int le,int ri,int id){ //l,r区间端点,le,ri待查询区间
if(le>ri) return -2e9;
int ans=-2e9;
if(l>=le&&r<=ri) return tree[id].maxx;
int mid=(l+r)>>1;
if(le<=mid&&tree[id].ls) ans=max(ans,query(l,mid,le,ri,tree[id].ls));
if(ri>mid&&tree[id].rs) ans=max(ans,query(mid+1,r,le,ri,tree[id].rs));
return ans;
}
int sum(int l,int r,int le,int ri,int id){
if(le>ri) return 0;
int ans=0;
if(le<=l&&ri>=r) return tree[id].sum;
int mid=(l+r)>>1;
if(le<=mid&&tree[id].ls) ans+=sum(l,mid,le,ri,tree[id].ls);
if(ri>mid&&tree[id].rs) ans+=sum(mid+1,r,le,ri,tree[id].rs);
return ans;
}
}data;
int n,m;
int a,b,year,water,L=-1e9,R=1e9;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d",&year,&water);
data.update(L,R,year,water,1);
}
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d %d",&a,&b);
if(b<a) {printf("false\n");continue;}
int le=data.sum(L,R,a,a,1),ri=data.sum(L,R,b,b,1),mid=data.sum(L,R,a+1,b-1,1);
int c=data.query(L,R,a,a,1),d=data.query(L,R,b,b,1),e=data.query(L,R,a+1,b-1,1);
if(le&&ri){
if(d<=c){
if(mid==b-a-1&&e<d) printf("true\n");
else if(e<d) printf("maybe\n");
else printf("false\n");
}else printf("false\n");
}else if(le){
if(e<c) printf("maybe\n");
else printf("false\n");
}else if(ri){
if(e<d) printf("maybe\n");
else printf("false\n");
}else printf("maybe\n");
}
}
#include<bits/stdc++.h>
using namespace std;
const int maxn=50000+10;
int n,q,a[maxn],dp[maxn][20],year[maxn],l,r;
map<int,int>mp;
void rmq()
{
for(int i=1;i<=n;i++) dp[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++){
for(int i=1;(i+(1<<j)-1)<=n;i++){
dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
}
}
}
int query(int le,int ri)
{
if(le>ri) return 0;
int k=0;
while(le+(1<<(k+1))-1<=ri) k++;
return max(dp[le][k],dp[ri-(1<<k)+1][k]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d %d",&year[i],&a[i]);
mp[year[i]]=i;
}
rmq();scanf("%d",&q);
while(q--){
scanf("%d %d",&l,&r);
int idl=mp[l],idr=mp[r];
if(idl&&idr){
int k=query(idl+1,idr-1);
if(idr-idl==r-l&&k<a[idr]&&a[idr]<=a[idl]) puts("true");
else if(idr-idl<r-l&&k<a[idr]&&a[idr]<=a[idl]) puts("maybe");
else puts("false");
}else if(idl&&!idr){
int id=lower_bound(year+1,year+n+1,r)-year-1;
int k=query(idl+1,id);
if(k>=a[idl]) puts("false");
else puts("maybe");
}else if(!idl&&idr){
int id=lower_bound(year+1,year+n+1,l)-year;
int k=query(id,idr-1);
if(a[idr]>k) puts("maybe");
else puts("false");
}else{
puts("maybe");
}
}
}