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;
}
bzoj1067——SCOI2007降雨量(線段樹,細節題)