[SCOI2007]降雨量

来源:互联网 发布:qq群的淘宝客都是团队 编辑:程序博客网 时间:2024/05/29 17:57

题目描述


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


输入输出格式


输入格式:
输入仅一行包含一个正整数n,为已知的数据。以下n行每行两个整数yi和ri,为年份和降雨量,按照年份从小到大排列,即yi<yi+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

题目


要是你第一眼就觉得是一道线段树的题,那怕是没救了(虽然也可以做),显然我们发现这道题没有修改操作,我们就会想到st表或者前缀和。这道题是求区间最大值,所以果断使用st表。然而瞎操作一波发现并没有那么简单。这道题难的地方和数据结构没啥关系,主要是判断十分恶心。各种条件判断不写对拍根本想不到啊。而且这组样例水得不能再水了。后面询问的年份可以不在前面出现,看看这组样例:1 2000 2000 1 1999 2001,应该输出maybe,然而我第一次re了(偷笑)。所以情况特别多啊,这个等会儿在程序里讲。还有一个点就是离散化,这个很简单数据保证有序,连序都不用排了。然后亲测一个隐藏条件:保证输入询问y1<y2。

代码如下(第一次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,不满足为1int 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(n logn)。






原创粉丝点击