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\),可以依据以下三个规律进行递推:
-
如果\(n-i+1\)在\(a\)序列中不存在,则所有\(\le i\)的长度都无法实现全排列(如输入样例\(3,3,2\))。
-
如果\(n-i+1\)在\(a\)序列中出现超过\(1\)次,则在所有\(<i\)的长度中\(n-i+1\)一定会作为最小值出现超过一次,也无法实现全排列(如输入样例\(1,3,2,1\))。
-
\(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;
}