詢問(構造,一種新思路)

cn是大帅哥886發表於2024-08-15

給一個長度為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


*/

  

相關文章