[SCOI2007]降雨量
题面
题目描述
我们常常会说这样的话:“ \(X\) 年是自 \(Y\) 年以来降雨量最多的”。它的含义是 \(X\) 年的降雨量不超过 \(Y\) 年,且对于任意 \(Y<Z<X\),\(Z\) 年的降雨量严格小于 \(X\) 年。例如 \(2002,2003,2004\) 和 \(2005\) 年的降雨量分别为 \(4920,5901,2832\) 和 \(3890\),则可以说“ \(2005\) 年是自 \(2003\) 年以来最多的”,但不能说“ $2005 $年是自 \(2002\) 年以来最多的”由于有些年份的降雨量未知,有的说法是可能正确也可以不正确的。
输入格式
输入仅一行包含一个正整数 \(n\),为已知的数据。
以下 \(n\) 行每行两个整数 \(t_i\) 和 \(c_i\),为年份和降雨量,按照年份从小到大排列,即 \(t_i<t_{i+1}\)。
下一行包含一个正整数 \(m\),为询问的次数。
以下 \(m\) 行每行包含两个数 \(P\) 和 \(Q\) ,即询问“ \(Q\) 年是自 \(P\) 年以来降雨量最多的。”这句话是必真、必假还是“有可能”。
输出格式
对于每一个询问,输出true
,false
或者maybe
。
输入输出样例
输入
6
2002 4920
2003 5901
2004 2832
2005 3890
2007 5609
2008 3024
5
2002 2005
2003 2005
2002 2007
2003 2007
2005 2008
输出
false
true
false
maybe
false
说明/提示
\(100\%\) 的数据满足:\(1\leqslant n\leqslant50000\), \(1\le m\le10000\), \(-10^9\leqslant y_i\leqslant10^9\), \(1\leqslant r_i\leqslant10^9\)。
说句闲话
没错,你没看错,为了契合我神奇的变量名,我改题面了
Solution
我们将题意简化。
假设有这么一串年份: t[1] t[2] t[3] t[4] t[5]
我们要判断的是“t[5]
年是否为自t[1]
以来降雨量最多的”。
令 ans=max(r[2],r[3],r[4])
,分类讨论。
- 如果
r[5]
已知,且ans>=r[5]
时,不满足题目中的 “\(Z\) 年的降雨量严格小于 \(X\) 年”,输出false
。 - 如果
r[1]
已知,且ans>=r[1]
时:- 如果
ans<r[5]
,因为r[5]>ans>=r[1]
,不满足题目中的“\(X\) 年的降雨量不超过 \(Y\) 年”,输出false
。 - 如果
ans>=r[5]
,不满足题目中的 “\(Z\) 年的降雨量严格小于 \(X\) 年”,输出false
。
- 如果
- 如果
r[5]
和r[1]
已知,且r[5]>r[1]
时,不满足题目中“ \(X\) 年的降雨量不超过 \(Y\) 年”,输出false
。 - 以上即为所有输出
false
时可能的情况。当上述所有条件都不满足时:t[1]~t[5]
中任意一个年份的降雨量未知时,输出maybe
。- 否则输出
true
。
代入到题目中来。
\(ans\) 可以用线段树或ST表求得,这里我们以线段树为例子。(其实是因为线段树码量大看起来逼格更高)
首先,询问的起点年份(stt
)和终点年份(end
)是否已知是一切的关键。
我们怎么知道 \(p\) 和 \(q\) 有没有在已知降雨量的年份中出现过?
输入格式中给出了一个重要条件:
按照年份从小到大排列,即 \(t_i<t_{i+1}\) 。
知道年份有序有什么用?
有序 \(\to\) 单调性 \(\to\) 二分 \(\to\) lower_bound
!
int getid(int yr){
return lower_bound(t+1,t+n+1,yr)-t;
}
查找第一个大于等于某一年份的已知年份的下标,还是分类讨论,设 x=getid(p),y=getid(q)
。
-
当查找到的年份等于需要查找的年份(
t[x]==p,t[y]==q
)当前年份的降雨量已知。
因为需要
Query
的是 \((p,q)\),我们将线段树查询的起点 \(beg\) 设为 \(x+1\) ,因为t[x+1]>t[x]
。 -
当查找到的年份大于需要查找的年份(
t[x]>p,t[y]>q
)当前年份的降雨量未知。
因为
t[x],t[y]
一定是已知的第一个 \(>p,>q\) 的年份,我们将起点 \(beg\) 设为 \(x\)。
read(n);
for(int i=1;i<=n;++i){
read(t[i]);
read(c[i]);
}
build(1,1,n);
----------------------------------
bool bg=1;
bool ed=1;
//标记查询起点和终点是否已知,已知为ture
read(p);
read(q);
int x=getid(p);
int y=getid(q);
int beg=x;
//线段树的查询起点
int stt=t[x],end=t[y];
//查询起点和终点的对应年份
int bgr=c[x],edr=c[y];
//查询起点和终点的对应降雨量
if(p!=stt)bg=0;
else beg++;
if(q!=end)ed=0;
int ans=Query(1,beg,y-1);
//(p,q)中降雨量的最大值
然后按照刚才的一系列操作,分别判断 false
,maybe
和true
。
关于 maybe
的判断,莫非要 \(p\sim q\) 循环一次判断是否全部已知?
你逗我!明明有 \(O(1)\) 的好不好!
我们已知:
p,q,p-q
:起点点份,终点年份,两个年份间一共存了多少个年份
x,y,x-y
:起点年份下标,终点年份下标,两个年份间一共存了多少个已知年份
若 p-q==x-y
(q-p,y-x
也行,只要能对应的上),也就是中间的全部年份数量==中间已知的全部年份数量。。。
那不就行了嘛。
Code
#include<cmath>
#include<cstdio>
#include<algorithm>
#define int long long
using std::lower_bound;
const int maxn=50005;
const int inf=1e18;
bool mb;
int n,m,p,q;
int t[maxn],c[maxn];
struct Segment_tree{
int mx;
int l,r;
}a[maxn<<2];
int max(int x,int y){return x>y?x:y;}
void read(int&x){
x=0;
bool f=0;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')f=1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=x*10+(ch^48);
ch=getchar();
}
if(f)x=-x;
return;
}
inline int getid(int yr){
return lower_bound(t+1,t+n+1,yr)-t;
}
int obzmax(int x,int y){return x>y?x:y;}
void build(int p,int l,int r){
a[p].l=l,a[p].r=r;
if(l==r){
a[p].mx=c[l];
return;
}
int mid=l+r>>1;
int lt=p*2;int rt=lt|1;
build(lt,l,mid);
build(rt,mid+1,r);
a[p].mx=obzmax(a[lt].mx,a[rt].mx);
return;
}
int Query(int p,int l,int r){
if(a[p].l>=l&&a[p].r<=r)
return a[p].mx;
int val=-inf;
int mid=a[p].l+a[p].r>>1;
int lt=p*2;int rt=lt|1;
if(l<=mid)
val=obzmax(val,Query(lt,l,r));
if(r>mid)
val=obzmax(val,Query(rt,l,r));
return val;
}
signed main(){
read(n);
for(int i=1;i<=n;++i){
read(t[i]);
read(c[i]);
}
build(1,1,n);
read(m);
while(m--){
bool bg=1;
bool ed=1;
read(p);
read(q);
int x=getid(p);
int y=getid(q);
int beg=x;
int stt=t[x],end=t[y];
int bgr=c[x],edr=c[y];
if(p!=stt)bg=0;
else beg++;
if(q!=end)ed=0;
int ans=Query(1,beg,y-1);
if(bg&&ans>=bgr)
puts("false");
else if(ed&&ans>=edr)
puts("false");
else if(bg&&ed&&bgr<=edr)
puts("false");
else if(!bg||!ed||p-q!=x-y)
puts("maybe");
else puts("true");
}
return 0;
}
end.
—— · EOF · ——
真的什么也不剩啦 😖