https://codeforces.com/contest/1450/problem/D
D题做晚了啊草,C想了一年还是不会啊
我们求出每个a[i]他作为最小值的范围l[i],r[i],我们令一段区间中最小值出现的为这段区间最小值出现的最左边或最右边,那么用单调栈左闭右开或者左开右闭就可以求得了
len[i]=r[i]-l[i]+1,也就是说a[i]作为最小值出现时,长度最大是len[i],再大a[i]就被别人给消除了
我们从大到小枚举ans[i]
如果n-i+1这个数字没有,说明后面也一定都没这个数字,直接break
如果有len[i]=i的,就给res[i]++,然后维护一个权值为下标的树状数组,如果res[i]=1,那么就是1,否则为0
在树状数组上二分求前缀全是1的最右边在x,如果x>=n-i+1,说明1到 n-i+1都恰好出现一次,ans[i]=1
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=3e5+10;
int n,m,k,cnt,tot,cas,mex;
int a[maxl],ans[maxl],l[maxl],r[maxl],s[maxl];
int b[maxl],res[maxl];
bool vis[maxl];
vector<int> in[maxl];
inline void prework()
{
scanf("%d",&n);
for(int i=1;i<=n+1;i++)
{
vis[i]=false,ans[i]=0;
in[i].clear();
}
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]),vis[a[i]]=true;
}
int top=0;s[0]=n+1;
for(int i=n;i>=1;i--)
{
while(top>0 && a[i]<=a[s[top]])
top--;
r[i]=s[top]-1;
s[++top]=i;
}
top=0;s[0]=0;
for(int i=1;i<=n;i++)
{
while(top>0 && a[i]<a[s[top]])
top--;
l[i]=s[top]+1;
in[r[i]-l[i]+1].push_back(a[i]);
s[++top]=i;
}
}
inline void add(int i,int x)
{
while(i<=n+1)
{
b[i]+=x;
i+=i&-i;
}
}
inline int find()
{
int sum=0,num=0;
for(int i=20;i>=0;i--)
if(num+(1<<i)<=n+1 && sum+b[num+(1<<i)]==num+(1<<i))
num+=1<<i,sum+=b[num];
return num;
}
inline void mainwork()
{
for(int i=0;i<=n+1;i++) b[i]=0,res[i]=0;
for(int i=n;i>=1;i--)
{
if(!vis[n-i+1])
break;
for(int d:in[i])
{
++res[d];
if(res[d]==1)
add(d,1);
if(res[d]==2)
add(d,-1);
}
int x=find();
if(x>=(n-i+1))
ans[i]=1;
}
}
inline void print()
{
for(int i=1;i<=n;i++)
printf("%d",ans[i]);
puts("");
}
int main()
{
int t=1;
scanf("%d",&t);
for(cas=1;cas<=t;cas++)
{
prework();
mainwork();
print();
}
return 0;
}