BZOJ 1067[SCOI 2007]

1067: [SCOI2007]降雨量

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 3301   Solved: 850
[ Submit][ Status][ Discuss]

Description

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

Input

输入仅一行包含一个正整数n,为已知的数据。以下n行每行两个整数yi和ri,为年份和降雨量,按照年份从小到大排列,即yi<yi+1。下一行包含一个正整数m,为询问的次数。以下m行每行包含两个数Y和X,即询问“X年是自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, -10^9<=yi<=10^9, 1<=ri<=10^9




分析:

看到这么大的数据,就可以非常显然的想到是线段树,但是本题有非常多的限制和判断:

true的情况需要满足

  x与y的值都已知且y值<x值且x+1到y-1都已知并且都小于y值

maybe满足

 1.x值y值均未知

 2.已知x值未知y值并且x+1到y-1都已知并且都小于y值

 3.已知y值未知x值并且x+1到y-1都已知并且都小于x值

 4.x为年份最大一年,y>x

 5.y为年份最小一年,x<y

 6.x,y均已知且y<x并且x+1到y-1有未知并且都小于x值

其它都是false(源自:http://hzwer.com/1655.html


下面是代码;

#include<cstdio>
#include<iostream>
using namespace std;

struct data{int ly,ry,mx,know;}tr[200001];//ly,ry是线段树维护的范围
//mx表示从ly到ry年中降雨量的最大值,know表示从ly到ry年的降雨量是否都知道
int n,m; 

void build(int k,int l,int r)//建树
{
  if(l==r)
  {
  	scanf("%d%d",&tr[k].ly,&tr[k].mx);
  	tr[k].ry=tr[k].ly;
  	tr[k].know=1;
  	return;
  }
  int mid=(l+r)>>1;
  build(k<<1,l,mid);
  build(k<<1|1,mid+1,r);
  tr[k].know=(tr[k<<1].know && tr[k<<1|1].know);
  if(tr[k<<1].ry+1!=tr[k<<1|1].ly)tr[k].know=0;
  tr[k].ly=tr[k<<1].ly;
  tr[k].ry=tr[k<<1|1].ry;
  tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);
}

int get(int k,int x)//找到x年的降雨量
{
  if(tr[k].ly==tr[k].ry)
  {
    if(tr[k].ly!=x)return 0;
    else return tr[k].mx;
  }
  if(tr[k<<1].ry>=x)return get(k<<1,x);
    else
      if(tr[k<<1|1].ly<=x)return get(k<<1|1,x);
  return 0;
}

int getnext(int k,int x)
{
  int l=tr[k].ly,r=tr[k].ry;
  if(l==r)return tr[k].ly;
  if(x<tr[k<<1].ry)return getnext(k<<1,x);
  else 
  	return getnext(k<<1|1,x);
}

int getlast(int k,int x)
{
  int l=tr[k].ly,r=tr[k].ry;
  if(l==r)return tr[k].ly;
  if(x>tr[k<<1|1].ly)return getlast(k<<1|1,x);
  else return getlast(k<<1,x);
}

int ask(int k,int x,int y,int num)//判断是否有哪一年的降雨量大于需要判断的年份
{
  bool f=0;
  if(x<tr[k].ly){f=1;x=tr[k].ly;}
  if(tr[k].ly==x&&tr[k].ry==y)
  {
  	if(tr[k].mx>=num)return 0;
  	else if(tr[k].know&&!f)return 1;
  	else return 2;
  }
  if(y<=tr[k<<1].ry)
    return ask(k<<1,x,y,num);
  else
  	if(x>=tr[k<<1|1].ly) return ask(k<<1|1,x,y,num);
    else
      {
      	int t1=ask(k<<1,x,tr[k<<1].ry,num);
      	int t2=ask(k<<1|1,tr[k<<1|1].ly,y,num);
      	if(!t1||!t2)return 0;
      	else 
      	  if(tr[k<<1].ry+1!=tr[k<<1|1].ly)return 2;
      	  else
      	  	return 1;
      }
}



int main()
{
  int n;
  scanf("%d",&n);
  build(1,1,n);
  scanf("%d",&m);
  for(int i=1;i<=m;i++)
  {
  	int l,r;
  	scanf("%d%d",&l,&r);
  	if(r<l){printf("false\n");continue;}
  	int lnum=get(1,l),rnum=get(1,r);
  	if(!lnum && !rnum)printf("maybe\n");
  	else
  	  {
  	  	int s=getnext(1,l),t=getlast(1,r);
  	  	if(!lnum)
  	  	{
  	  	  if(s>t||r==t){printf("maybe\n");continue;}
  	  	  int f=ask(1,s,t,rnum);
  	  	  if(f==0)printf("false\n");
  	  	  else printf("maybe\n");
  	  	}
  	  	else 
  	  	  if(!rnum)
  	  	  {
  	  	  	if(s>t||l==s){printf("maybe\n");continue;}
  	  	  	int f=ask(1,s,t,lnum);
  	  	  	if(f==0)printf("false\n");
  	  	  	else printf("maybe\n");
  	  	  }
  	  	  else
  	  	  {
  	  	  	if(rnum>lnum){printf("false\n");continue;}
  	  	  	if(s>t)
  	  	  	{
  	  	  	  if(l+1==r)printf("true\n");
  	  	  	  else printf("maybe\n");
  	  	  	  continue;
  	  	  	}
  	  	  	int f=ask(1,s,t,rnum);
  	  	  	if(f==0)printf("false\n");
  	  	  	else 
  	  	  	  if(f==1)
  	  	  	  {
  	  	  	  	if(l+1==s&&r-1==t)printf("true\n");
  	  	  	  	else printf("maybe\n");
  	  	  	  }
  	  	  	  else if(ask(1,s,t,rnum)==2)printf("maybe\n");
  	  	  	  else printf("false\n");
  	  	  }
  	  }
  }
  return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值