题目描述
我们常常会说这样的话:“X年是自Y年以来降雨量最多的”。它的含义是X年的降雨量不超过Y年,且对于任意Y < < <script type="math/tex" id="MathJax-Element-11"><</script>Z < < <script type="math/tex" id="MathJax-Element-12"><</script>X,Z年的降雨量严格小于X年。例如2002,2003,2004和2005年的降雨量分别为4920,5901,2832和3890,则可以说“2005年是自2003年以来最多的”,但不能说“2005年是自2002年以来最多的”由于有些年份的降雨量未知,有的说法是可能正确也可以不正确的。
输入格式:
输入仅一行包含一个正整数 n n ,为已知的数据。以下行每行两个整数 yi y i 和 ri r i ,为年份和降雨量,按照年份从小到大排列,即 yi<yi+1 y i < y i + 1 。下一行包含一个正整数 m m ,为询问的次数。以下行每行包含两个数Y和X,即询问“X年是自Y年以来降雨量最多的。”这句话是必真、必假还是“有可能”。
输出格式:
对于每一个询问,输出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<=n<=50000,1<=m<=10000,−109<=yi<=109,1<=ri<=109 1 <= n <= 50000 , 1 <= m <= 10000 , − 10 9 <= y i <= 10 9 , 1 <= r i <= 10 9
要是你第一眼就觉得是一道线段树的题,那怕是没救了(虽然也可以做),显然我们发现这道题没有修改操作,我们就会想到st表或者前缀和。这道题是求区间最大值,所以果断使用st表。然而瞎操作一波发现并没有那么简单。这道题难的地方和数据结构没啥关系,主要是判断十分恶心。各种条件判断不写对拍根本想不到啊。而且这组样例水得不能再水了。后面询问的年份可以不在前面出现,看看这组样例:1 2000 2000 1 1999 2001,应该输出maybe,然而我第一次re了(偷笑)。所以情况特别多啊,这个等会儿在程序里讲。还有一个点就是离散化,这个很简单数据保证有序,连序都不用排了。然后亲测一个隐藏条件:保证输入询问 y1<y2 y 1 < y 2 。
代码如下(第一次st表就讲细一点):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
struct lxy{
int rain,year;
}data[100005];//存数据
int n,m;
int st1[100005][20];//判断区间最大值的下表
//记得st表数组开双倍
bool st2[100005][20];//判断区间是否为连续年份,满足为0,不满足为1
int lg[100005];//存每个数的log,向下取整
int er(int l,int r,int q)//查找是否存在年份为q,存在返回下表,否则返回0
{
int mid=(l+r)/2;
if(l==r)
return l;
if(data[mid].year<=q)
return er(mid+1,r,q);
if(data[mid].year>q)
return er(l,mid,q);
}
int find(int l,int r,int q)//查找年份大于q的最小值下标
{
if(l>r)
return 0;
int mid=(l+r)/2;
if(q==data[mid].year)
return mid;
if(data[mid].year<q)
return find(mid+1,r,q);
else
return find(l,mid-1,q);
}
int ques(int x1,int x2)//查询函数,false返回1,true返回2,maybe返回3
{
int y1=find(1,n,x1),y2=find(1,n,x2);
if(y1==0&&y2==0)//如果两个询问年份都不知道,输出maybe
return 3;
int z1=er(1,n,x1),z2=er(1,n,x2);
while(data[z2].year!=x2&&data[z2].year>=x2)//z2是不超过x2的最大年份下标
if(z1>z2)//如果满足,一定是maybe,可以自行脑补
return 3;
int p=lg[z2-z1+1];
int w;
if(data[st1[z1][p]].rain<data[st1[z2-(1<<p)+1][p]].rain)//查找区间最大值的下标
w=st1[z2-(1<<p)+1][p];
else
w=st1[z1][p];
if(y2!=0&&w!=z2)//如果x2知道且最大值下标不为x2,输出false
return 1;
if(y1!=0)//如果x1知道且x1降雨量比区间最大值小,输出false
if(data[y1].rain<=data[w].rain)
return 1;
if(y1==0||y2==0||st2[z1][p]==1||st2[z2-(1<<p)+1][p]==1)//如果区间是断的,输出maybe
return 3;
if(y1!=0)//如果x1存在,还要判断x1与区间是否连接
if(data[z1-1].year+1!=data[z1].year)
return 3;
return 2;//最后输出true
}
int main()
{
scanf("%d",&n);
int p=2;
int t=0;
for(int i=1;i<=n;i++)//算log,这样可以O(1)查询咯(虽然查询中的二分也让复杂度变成logn了)
{
if(i<p)
lg[i]=t;
else
{
p=p<<1;
t++;
lg[i]=t;
}
}
for(int i=1;i<=n;i++)
{
scanf("%d%d",&data[i].year,&data[i].rain);
st1[i][0]=i;
}
for(int j=1;j<=15;j++)//st表初始化,j大小数据范围:2^j<数据个数,不然容易超界
for(int i=1;i<=n;i++)
{
if(data[st1[i][j-1]].rain<data[st1[i+(1<<(j-1))][j-1]].rain)
st1[i][j]=st1[i+(1<<(j-1))][j-1];
else
st1[i][j]=st1[i][j-1];
if(data[i+(1<<(j-1))-1].year+1!=data[i+(1<<(j-1))].year)
st2[i][j]=1;
else
st2[i][j]=st2[i][j-1]|st2[i+(1<<(j-1))][j-1];
}
scanf("%d",&m);
for(int i=1;i<=m;i++)//查询询问
{
int x,y;
scanf("%d%d",&x,&y);
int ans;
ans=ques(x,y);
if(ans==1)
printf("false\n");
if(ans==2)
printf("true\n");
if(ans==3)
printf("maybe\n");
}
}
这样就完成啦,复杂度是 O(nlogn) O ( n l o g n ) 。