scoi2007降雨量

1067: [SCOI2007]降雨量

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 1096   Solved: 241
[ 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

Source

POJ 2637 WorstWeather Ever

bzoj评测链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1067

poj评测链接:http://poj.org/problem?id=2637

解法:

rmq问题,用st算法或线段树都ok,主要难的是对于各种条件的判断。我在这里讲的是st算法求解。用ma[i][j]代表区间[i][i+d[j]-1]内的最大值,flag[i][j]记录区间[i][i+d[j]-1]内是否存在降雨量未知的年份,用y[i]数组记录排在第i位的年份编号,用get(X)函数求得y【】数组从小到大,第一个大于等于x的位置。

1.为true的条件:

   ①Y<X;

   ②Y---->X年份的降雨量都已知;

   ③X年份的降雨量<=Y年份的降雨量;

   ④若get(X)-get(Y)==1,上述条件成立则为true

       若get(X)-get(Y)>1,Max记为从Y+1年到X-1年的最大降雨量,若Max<X年份的降雨量,则为true;

2.为false的条件:

  ①若Y>=X,则为false;

  ②若已知X与Y年份的降雨量,X年份的降雨量大于Y年份的降雨量,则为false

  ③若已知X与Y年份的降雨量,且get(X)-get(y)>1,即X,Y年份中间还有其他年份,Max记为从get(Y)+1到get(X)-1年份的最大降雨量,若Max>=X年份的降雨量,则为false;

  ④若仅仅已知X年份的降雨量,且get(X)-get(y)>1,Max记为Y数组中从get(Y)到get(x)-1年份的最大降雨量,Max>=X年份的降 雨量,则为false;

  ⑤若仅仅已知Y年份的降雨量,且get(X)-get(Y)>1,Max记为Y数组中从get(Y)+1到get(X)-1年份的最大降雨量,Max>=Y年份的降 雨量,则为false;

如果你在bzoj上AC了这道题,在poj上却A不过,那么请检查你是否在每读入一组新数据时,都把flag数组memset过,如果没有,加上试一试。

代码:

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cctype>
#define maxn (50000+100)
#define inf 2000000000
#define false {printf("false\n");continue;}
#define true  {printf("true\n");continue;}
#define maybe {printf("maybe\n");continue;}
using namespace std;

int n,m,y[maxn];
int d[17]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536};
int ma[maxn][17];
bool flag[maxn][17];//1 代表区间存在不连续的部分 

void init()
{
  freopen("rain.in","r",stdin);
  freopen("rain.out","w",stdout);
}

inline int getin()
{
  int ans=0;bool sign=0;char tmp;
  do tmp=getchar();
  while(!isdigit(tmp) && tmp!='-');
  if(tmp=='-')sign=1,tmp=getchar();
  do ans=(ans<<3)+(ans<<1)+tmp-'0';
  while(isdigit(tmp=getchar()));
  return sign?-ans:ans;
}

void build()
{
  int i,j; n=getin();
  for(i=1;i<=n;i++)y[i]=getin(),ma[i][0]=getin();
  for(i=1;i<n;i++)if(y[i]+1!=y[i+1])flag[i][1]=1;
  y[n+1]=inf;
  
  for(j=1;d[j]<=n;j++)
    for(i=1;i+d[j]-1<=n;i++)
      ma[i][j]=max(ma[i][j-1],ma[i+d[j-1]][j-1]);

  for(j=2;d[j]<=n;j++)
    for(i=1;i+d[j]-1<=n;i++)
      if(flag[i][j-1] || flag[i+d[j-1]][j-1] || y[i+d[j-1]-1]+1!=y[i+d[j-1]])
        flag[i][j]=1;          
}

inline int get(int x)
{
  int l=1,r=n,m;
  while(l<=r)
    {
      m=(l+r)>>1;
      if(x<=y[m])r=m-1;
      else l=m+1;
    }
  return l;  
}

void work()
{
  build();
  
  m=getin();
  int i,a,b,k,Max,aa,bb;
  for(i=1;i<=m;i++)
    {
      aa=getin(),bb=getin();
      if(bb<=aa)false
      a=get(aa),b=get(bb);
      if(y[a]==aa && y[b]==bb)
        {
          if(ma[b][0]>ma[a][0])false
          if(b-a==1)
            {if(!flag[a][1])true}
          else
            {  
              k=int(log((b-a-1)*(1.0))/log(2.0));
              Max=max(ma[a+1][k],ma[b-d[k]][k]);
              if(Max>=ma[b][0])false
              k=int(log((b-a+1)*(1.0))/log(2.0));
              if(!flag[a][k] && !flag[b-d[k]+1][k] && !flag[a+d[k]-1][1])true
            }
        }
      if(y[a]==aa && y[b]!=bb && b-a>=2)
        {
          k=int(log((b-a-1)*(1.0))/log(2.0));
          Max=max(ma[a+1][k],ma[b-d[k]][k]);
          if(Max>=ma[a][0])false;
        }  
      if(y[a]!=aa && y[b]==bb && b-a>=1) 
        {
          k=int(log((b-a)*(1.0))/log(2.0));
          Max=max(ma[a][k],ma[b-d[k]][k]);
          if(Max>=ma[b][0])false;
        } 
      maybe;  
    }
}  


int main()
{
  init();
  work(); 
  return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值