降雨量
P2471 [SCOI2007] 降雨量
这题的核心不是 ST
表,而是讨论。
大体思路
我们先找 \(Y,X\) 两个位置(不太习惯,下面用 \(A,B\) 表示),记录是否确定值。
接着找 \((A,B)\) 区间内确定值的位置,做一次查询最大值。
接下来就是分类了:
- false
- 如果 \(A,B\) 确定,并且 \(W_B>W_A\)(\(W\) 表示降水量)。
- 中间有值,并且 \(A\) 确定,\(B\) 不确定,中间最大值不小于 \(A\),那么可以推断出 \(B\) 不可能作为最大值(因为它的值一定不超过 \(A\))。
- 中间的值不小于 \(W_B\)。
- true:\(A,B\) 确定,中间要么没有,要么中间的值都是确定的,并且最大值小于 \(W_B\)。
- maybe:其余情况都是。
维护最值个人喜欢用 ST
表,原因是不容易打挂,而且查询复杂度小,代码短,完全优于线段树。
#include<cstdio>
#include<algorithm>
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while
const int N=50010,INF=2e9,M=16;
int n,y[N],w[N],m,f[M][N],LOG[N];//y年份 r雨量
void RMQ(){
E(i, n)f[0][i]=w[i];
E(i, LOG[n]){
for(int j=1;j+(1<<i)-1<=n;++j)
f[i][j]=max(f[i-1][j],f[i-1][j+(1<<i-1)]);
}
}
int find(int x){//>=x的第一个位置
int l=1,r=n+1;
Wh(l<r){
int mid=l+r>>1;
if(y[mid]>=x)r=mid;
else l=mid+1;
}
return l;
}
int Find(int x){//<=x的最后一个位置
int l=0,r=n;
Wh(l<r){
int mid=l+r+1>>1;
if(y[mid]<=x)l=mid;
else r=mid-1;
}
return l;
}
int query(int l,int R){
if(l>R)return -INF;
int k=LOG[R-l+1];
return max(f[k][l],f[k][R-(1<<k)+1]);
// int maxn=0;
// Le(i, l, R)
// maxn=max(w[i],maxn);
// return maxn;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
scanf("%d",&n);
LOG[1]=0;
Le(i, 2, n)LOG[i]=LOG[i>>1]+1;
E(i, n)scanf("%d%d",y+i,w+i);
y[0]=-INF;
y[n+1]=INF;
RMQ();
scanf("%d",&m);
E(i, m){
int a,b;
scanf("%d%d",&a,&b);
int BP=find(b),AP=find(a);
bool HB=y[BP]==b,HA=y[AP]==a;
int posa=find(a+1),posb=Find(b-1);
bool Hmid=a+1<=b-1;
int tmp=query(posa,posb);
//false
if(HA&&HB&&w[AP]<w[BP]||Hmid&&HA&&!HB&&tmp>=w[AP]||Hmid&&HB&&tmp>=w[BP])
puts("false");
else if(HA&&HB&&(!Hmid||posb-posa+1==b-a-1&&tmp<w[BP]))
puts("true");
else
puts("maybe");
}
return 0;
}