給一個長度為n的正整數陣列a[1,2,...n],一個長度為m的查詢陣列q[1,2,...m],q[i]=(val,minlen,maxlen)。請你按輸入順序處理這m次查詢,對於第i次查詢q[i[=(val,minlen,maxlen);請你輸出是否存在一個a的子陣列s滿足min(s)≥val且s的長度在minlen和maxlen之間。
輸入格式
第一行輸入一個正整數n
第二行有n個正整數,a[1,2,...n]
第三行輸入一個正整數m
接下來m行,每行三個正整數,表示q[i]的三個數,val,minlen,maxlen
1<=n,m<=1e5
1<=val,a[i]<=1e9
1<=minlen<=maxlen<=n
輸出格式
輸出m行,如果存在輸出“Yes”,否則輸出“No”
輸入/輸出例子1
輸入:
3
1 3 2
6
1 1 3
1 1 2
3 2 2
3 1 2
4 1 1
2 1 2
輸出:
Yes
Yes
No
Yes
No
Yes
樣例解釋
無
很有意思的一道壓軸題。
對於正面無法很好回答的多次詢問,我們可以自己構造答案,再對應詢問給出構造答案
構造部分
首先可以發現,在一個區間中刪去某些數,min肯定不會減小,所以maxlen沒用,我們只需要minlen
比如{1,2,3,4}中的min是1,我們刪去{3,4},min還是1,刪去{1,2},min是3,無論怎麼樣,min不會減小,但是可能會增加
對於每個a[i],我們都可以構造一個答案,假設構造的區間是[L, R],只要L~R區間內每個數都>=a[i],就可以滿足構造條件,使得這個區間內的最小值是a[i]
我們構造的答案區間長度就可以確定下來。我們現在可以嘗試用構造的這些答案去跟詢問一一對應
所以我們構造的答案包含兩個值,一個是答案區間長度(後續稱為len);一個是a[i],也就是區間中的min值,我們假定這個構造的答案是 f[i]
構造部分就結束了,接下來我們考慮如何把構造出來的答案對應上詢問
對應詢問部分
我們對這些f[i]排個序,從小到大,按照min去排序,這樣就對於後來詢問所給出的val,我們就可以考慮去這些答案區間中二分查詢min值
二分f[i].min>=val,如何看看是否滿足題意,f[i].len>=minlen,即可
由於排序後,min是從小到大的,根據第一點小發現,我們可以對len搞一個長度的字尾最大值,這樣才可以保證滿足答案
為什麼可以這樣?題目只是要求min(s)≥val就行,那麼比min大的數肯定也是可以滿足要求的,但是比min大的數它的最終區間長度可能比min要大!
例如排完序後的f[i]是這樣的:
f[1]={1, 3}
f[2]={2, 2}
f[3]={3, 3}
我們先在求val=2,minlen=3
但是我們二分查表發現f[2].min>=val,但是 f[2].len=2,是不滿足的,會被誤判是”No“
但是答案其實是”Yes“,因為 f[3].min>=val,也是滿足題目要求”min(s)>=val,所以f[3]的長度對這個答案也是有影響的,所以我們要整一個字尾最大值
我們看看字尾最大值是否>=minlen,是的話就是"Yes",反之亦然
這裡的時間複雜度是 O(logn),僅有一個二分而已
我們再考慮回去
如何減小構造的時間複雜度呢?暴力就是O(n^2)
我們可以不用一一列舉,用單調佇列可以預處理(找第一個<a[i]的數,找極值就是單調佇列嘛!),也就是 O(n)
這裡給出O(n^2 log n)複雜度的做法
#include <bits/stdc++.h> using namespace std; const int N=1e5+5; struct node { int min, len; }b[N]; int n, a[N], m, val, Minlen, Maxlen; /* int find(int L, int R, int x) { for (int i=R; i>=L; i--) if (a[i]<a[x]) return i+1; return L; }*/ bool cmp(node a, node b) { return a.min<b.min; } int main() { scanf("%d", &n); for (int i=1; i<=n; i++) scanf("%d", &a[i]); scanf("%d", &m); for (int i=1; i<=n; i++) { int sum=0; // L=find(1, i, i), R=find(i+1, n, i); for (int j=i-1; j>=1; j--) { if (a[j]<a[i]) break; sum++; } // if (i==2) printf("{%d}", sum); for (int j=i+1; j<=n; j++) { if (a[j]<a[i]) break; sum++; } b[i].min=a[i], b[i].len=sum+1; } sort(b+1, b+1+n, cmp); for (int i=n-1; i>=1; i--) b[i].len=max(b[i].len, b[i+1].len); /* printf("\n"); for (int i=1; i<=n; i++) printf("%d %d\n", b[i].min, b[i].len); printf("\n"); */ while (m--) { scanf("%d%d%d", &val, &Minlen, &Maxlen); int L=1, R=n, x=0; while (L<=R) { int mid=(L+R)>>1; if (b[mid].min>=val) x=mid, R=mid-1; else L=mid+1; } //printf("{%d}", x); if (b[x].min<val) printf("No\n"); else { if (b[x].len>=Minlen) printf("Yes\n"); else printf("No\n"); } } return 0; } /* 3 1 3 2 1 3 1 2 */