bzoj1067——SCOI2007降雨量(线段树,细节题)

题目描述

我们常常会说这样的话:“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。

简单来说!对于一个询问来说\(x,y\),我们需要满足\(x\ge y>z 其中z\in[x+1,y-1]\)

一眼看过去QwQ

这个题难道不是区间维护最大值,不就OK了吗?

一写,发现完美gg!!

进入正题:

首先我们发现年份是非常大的,所以需要将离散化,同时又方便我们统计有没有未知的年份\(maybe\)的

那么我们就从小到大依次将年份标号为\(1 - n\),然后如果当前的年份比前一个年份大1以上,那么就将给他赋一个1的权值

那么我们统计两个年份之间有没有未知的年的时候,我们需要求一个区间和,就可以得知了

接下来是处理询问,首先我们要知道询问种给定的两个年份不一定是都知道的

那么我们应该怎么判断这个年份是不是知道的呢?

只需要开一个数组,记录所有出现的年份,然后\(lower_bound\)一下,看一下和它本身一不一样就行了

int getpos(int x)
{
   if (x<ss[1]) return ss[1];
   if (x>ss[n]) return ss[n];
   return ss[lower_bound(ss,ss+1+n,x)-ss];
}

所以需要分类讨论:

当$x!=getpos(x) 且 y!=getpos(y) \(的时候,一定是\)maybe$

当$x==getpos(x) 且 y!=getpos(y) $的时候,我们需要把y跳到第一个已知的年(就是比他小的最大的)

if (y>ss[n]) y=ss[n];
       else
         y=ss[lower_bound(ss,ss+1+n,y)-ss-1];

然后比较中间的数,是否都小于x,如果存在大于等于的x的年份,那一定是\(false\)否则就是\(maybe\)

当\(x!=getpos(x) 且 y==getpos(y)\)的时候,同理

当\(x==getpos(x) 且 y==getpos(y)\)的时候

我们就是要满足\(x\ge y>z 其中z\in[x+1,y-1]\)就可以,那么求一个中间区间的最大值,然后比较一下就可以

如果中间区间的query不等于区间长度,那就是maybe

我建议,就是先判断\(false\)接着判断\(true\)else就是\(maybe\)

一些细节之间看代码吧

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<map>
#include<vector>

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
  return x*f;
}

const int maxn = 300010;

struct Node{
    int mx,mn;
    int sum;
};

Node f[4*maxn];
int n,m;
int a[maxn];
int c[maxn];

void up(int root)
{
    f[root].mx=max(f[2*root].mx,f[2*root+1].mx);
    f[root].mn=min(f[2*root].mn,f[2*root+1].mn);
    f[root].sum=f[2*root].sum+f[2*root+1].sum;
}

void build(int root,int l,int r)
{
    if (l==r)
    {
        f[root].mn=f[root].mx=a[l];
        f[root].sum=c[l];
        return;
    }
    int mid = (l+r) >> 1;
    build(2*root,l,mid);
    build(2*root+1,mid+1,r);
    up(root);
}

int querymax(int root,int l,int r,int x,int y)
{
    if (l>r || x>y) return -2e9;
    if (x<=l && r<=y)
    {
        return f[root].mx;
    }
    int mid = (l+r) >> 1;
    int ans = -2e9;
    if (x<=mid) ans=max(ans,querymax(2*root,l,mid,x,y));
    if (y>mid) ans=max(ans,querymax(2*root+1,mid+1,r,x,y));
    return ans;
}

int querysum(int root,int l,int r,int x,int y)
{
    if (l>r || x>y) return 0;
   if (x<=l && r<=y)
   {
     return f[root].sum;
   }
   int mid = (l+r) >> 1;
   int ans = 0;
   if (x<=mid) ans+=querysum(2*root,l,mid,x,y);
   if (y>mid) ans+=querysum(2*root+1,mid+1,r,x,y);
   return ans;
}

int front;
int ss[maxn];

int getpos(int x)
{
   if (x<ss[1]) return ss[1];
   if (x>ss[n]) return ss[n];
   return ss[lower_bound(ss,ss+1+n,x)-ss];
}

int get(int x)
{
    if (x<ss[1]) return 1;
    if (x>ss[n]) return n;
    return lower_bound(ss,ss+1+n,x)-ss;
}

int main()
{
  scanf("%d",&n);
  c[1]=1;
  for (int i=1;i<=n;i++)
  {
      int x,y;
      scanf("%d",&x);
      scanf("%d",&a[i]);
      if (i!=1 && x-front==1) c[i]=1;
      ss[i]=x;
      front=x;
  }
  ss[++n]=2e9; ss[0]=-2e9;
  build(1,1,n);

  scanf("%d",&m);
 // cout<<querymax(1,1,n,2,4)<<endl;
  for (int i=1;i<=m;i++)
  {
     int x,y;
     x=read(),y=read();
     if (x!=getpos(x) && y!=getpos(y))
     {
        printf("maybe\n");
        continue;
     }
     if (x!=getpos(x))
     {
       x=getpos(x);
       int a1=querymax(1,1,n,get(x),get(y)-1);
       int cnt = a[get(y)];
       if (a1>=cnt) printf("false\n");
       else printf("maybe\n");
       continue;
    }
    if (y!=getpos(y))
     {
       if (y>ss[n]) y=ss[n];
       else
         y=ss[lower_bound(ss,ss+1+n,y)-ss-1];
       int a1=querymax(1,1,n,get(x)+1,lower_bound(ss,ss+1+n,y)-ss);
       //cout<<a1<<endl;
       int cnt = a[get(x)];
       if (a1>=cnt) printf("false\n");
       else printf("maybe\n");
       continue;
    }
    int l=get(x)+1,r=get(y);
    int cnt = a[get(y)];
    int cnt1=a[get(x)];
    int a1=querymax(1,1,n,get(x)+1,get(y)-1);
    int a3=querysum(1,1,n,get(x)+1,get(y));
    if (a1>=cnt || cnt>cnt1) printf("false\n");
    else if (a1<cnt && r-l+1==a3 && cnt<=cnt1) printf("true\n");
    else printf("maybe\n");
  }
  return 0;
}

原文地址:https://www.cnblogs.com/yimmortal/p/10160729.html

时间: 2024-10-11 19:35:38

bzoj1067——SCOI2007降雨量(线段树,细节题)的相关文章

bzoj1067 scoi2007 降雨量 RMQ+讨论

很水的题目 滚回来之后的第一道题 竟然做了1个多小时 最近的状态极其不好,这样下去有可能省队都进不了... 这个题算是RMQ的基础题了 写他就是为了练习RMQ的 首先预处理出来区间Max 我们注意到10^9的区间显然太大了,但是其中有用的点非常少,所以我们要离散出来 当然题目中其实已经给你排好序了... 所以在st 表建好以后,就每次读入然后二分找对应的离散后的相应坐标,然后o(1)就可以完成询问 具体的情况比较多 不过都听好想的 如果wa了看代码吧 #include <iostream> #

HDU 4893 线段树裸题

Wow! Such Sequence! Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 2512    Accepted Submission(s): 751 Problem Description Recently, Doge got a funny birthday present from his new friend, Pro

HDU 4027 Can you answer these queries? 线段树裸题

题意: 给定2个操作 0.把区间的每个数sqrt 2.求和 因为每个数的sqrt次数很少,所以直接更新到底,用个标记表示是否更新完全(即区间内的数字只有0,1就不用再更新了) #include<stdio.h> #include<iostream> #include<algorithm> #include<vector> #include<cmath> #include<queue> #include<set> #incl

[POJ2104] 区间第k大数 [区间第k大数,可持久化线段树模板题]

可持久化线段树模板题. #include <iostream> #include <algorithm> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <ctime> #include <vector> using namespace std; int n,q,tot,a[110000]; in

POJ 3468 线段树裸题

这些天一直在看线段树,因为临近期末,所以看得断断续续,弄得有些知识点没能理解得很透切,但我也知道不能钻牛角尖,所以配合着刷题来加深理解. 然后,这是线段树裸题,而且是最简单的区间增加与查询,我参考了ACdreamer的模板,在此基础上自己用宏定义来精简了一下代码: 1 #include<cstdio> 2 typedef long long LL; 3 #define root int rt, int l, int r 4 #define lson rt*2, l, mid 5 #define

[ACM] Color the ball [线段树水题][数组开大]

Description N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的"小飞鸽"牌电动车从气球a开始到气球b依次给每个气球涂一次颜色.但是N次以后lele已经忘记了第I个气球已经涂过几次颜色了,你能帮他算出每个气球被涂过几次颜色吗? Input 每个测试实例第一行为一个整数N,(N <= 100000).接下来的N行,每行包括2个整数a b(1 <= a <= b <= N).  当N

poj 2182 Lost Cows(线段树经典题)

题目链接:http://poj.org/problem?id=2182 Lost Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 9152   Accepted: 5879 Description N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, th

LA 2191电位计(线段树模板题)

线段树模板题,没啥好说的.....注意输出是case之间空一行就行.........之前一直没注意,一直wa 代码如下: #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #includ

【整理】线段树30题

1,poj 1151 Atlantis: 求矩形面积并. 2,poj 1177 Picture: 求矩形轮廓的周长. 3,poj 1389 Area of Simple Polygons :同第一题. 4,poj 1823 Hotel :线段树线段的插入删除求线段树中最长的线段长度 5,poj 2104 K-th Number:线段树维护归并排序树+三次二分查找   (区间第k大 ,主席树也行,前者可以练习试一下). 6,poj 2155 Matrix :求二维平面的矩形信息,二维线段树,或者二

Codeforces Round #393 (Div. 2) (8VC Venture Cup 2017 - Final Round Div. 2 Edition) E - Nikita and stack 线段树好题

http://codeforces.com/contest/760/problem/E 题目大意:现在对栈有m个操作,但是顺序是乱的,现在每输入一个操作要求你输出当前的栈顶, 注意,已有操作要按它们的时间顺序进行. 思路:线段树好题啊啊,我们把push当成+1, pop当成-1,按操作的位置建立线段树,那么如何 寻找栈顶呢,我们计算每个点的后缀,栈顶就是下标最大的>0的后缀,我们拿后缀建立线段树, 剩下的就是区间加减法,和求区间最大值啦. #include<bits/stdc++.h>