题解
将题意简化,对于(i)满足(1le ile n),设(b_j=a_j)到(a_{j+i-1})的最小值((1le jle 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("
");
}
return 0;
}