CF1450D Solution

题目链接

题解

将题意简化,对于\(i\)满足\(1\le i\le n\),设\(b_j=a_j\)\(a_{j+i-1}\)的最小值\((1\le j\le n-i+1)\),我们需要判断\(b\)序列是否为\(n-i+1\)的全排列。因为枚举每一个\(j\)的时间已经是\(O(n^2)\),而此题时间复杂度需要在\(O(nlogn)\)以内,所以只能考虑转移\(i\),可以依据以下三个规律进行递推:

  1. 如果\(n-i+1\)\(a\)序列中不存在,则所有\(\le i\)的长度都无法实现全排列(如输入样例\(3,3,2\))。

  2. 如果\(n-i+1\)\(a\)序列中出现超过\(1\)次,则在所有\(<i\)的长度中\(n-i+1\)一定会作为最小值出现超过一次,也无法实现全排列(如输入样例\(1,3,2,1\))。

  3. \(n-i+1\)在所有\(\ge n-i+1\)的值中只能处于开始或最后,若非如此在所有\(<i\)的长度中也会出现多次(如输入样例\(1,5,3,4,2\)中的\(\{5,3\}\)\(\{3,4\}\))。

\(a\)数组中从后向前枚举\(i\)即可。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a[N],sum[N];//sum[i]:i在a数组中出现的次数 
bool ans[N];
int main()
{
	int t,n;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n;i++) sum[i]=ans[i]=0;
		for(int i=1;i<=n;i++) {scanf("%d",&a[i]); sum[a[i]]++;}
		int l=1,r=n; ans[1]=1;//l/r:a数组中大于等于i的值的最左/右端
		for(int i=n;i>=1;i--)
		{
			int b=n-i+1;
			if(!sum[b]) break;//规律1
			ans[i]=1;
			if(sum[b]>1) break;//规律2
			if(a[l]!=b && a[r]!=b) break;//规律3
			if(a[l]==b)//维护l、r
				while(a[++l]<b);
			if(a[r]==b)
				while(a[--r]<b);
		}
		for(int i=1;i<=n;i++)
			if(sum[i]!=1) {ans[1]=0; break;}
		for(int i=1;i<=n;i++) printf("%d",ans[i]);
		printf("\n");
	}
	return 0;
}
posted @ 2021-01-01 18:29  violet_holmes  阅读(56)  评论(0编辑  收藏  举报