很明显我们可以转化一下看看,贪心的从最小的开始选择,每一个数字可以连向左边还没被扩进去的最大值,右边的第一个,或者是自己,维护区间最大值可以用线段树,维护哪些区间被用过可以用set。
则234被扎死口,234的值都是0;56未被扎死口,6的值是0。可以发现,被扎死口的元素值为0,值为0的元素不一定被扎死口,因为还可以向左连5,这里扎死口的概念就是能否越过这个点向左找值的意思
#include
#include
#include
#define INF 1e9
using namespace std;
const int N=100005;
set<int>s;int maxx[N*4],a[N],pos[N],n,ans[N];
void updata(int now){maxx[now]=max(maxx[now<<1],maxx[now<<1|1]);}
void change(int now,int l,int r,int x,int v)
{
if (l==r){maxx[now]=v;return;}
int mid=(l+r)>>1;
if (x<=mid) change(now<<1,l,mid,x,v);
else change(now<<1|1,mid+1,r,x,v);
updata(now);
}
int qurry(int now,int l,int r,int lrange,int rrange)
{
if (lrange>rrange) return 0;
if (lrange<=l && rrange>=r) return maxx[now];
int mid=(l+r)>>1,ans=0;
if (lrange<=mid) ans=qurry(now<<1,l,mid,lrange,rrange);
if (rrange>mid) ans=max(ans,qurry(now<<1|1,mid+1,r,lrange,rrange));
return ans;
}
void bl(int l,int r)
{
for (int i=l;i<=r;i++)
change(1,1,n,i,0),s.insert(i);
int z=r-l+1;
for (int i=0;i<=r-l;i++) ans[a[l+(i-1+z)%z]]=a[l+i];
}
int main()
{
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%d",&a[i]);
for (int i=1;i<=n;i++) pos[a[i]]=i;
for (int i=1;i<=n;i++) change(1,1,n,pos[i],i);
s.insert(0);
for (int i=1;i<=n;i++)
if (!ans[i])
{
int wz=0,now=pos[i],maxx=0,zuo;
wz=*--s.lower_bound(now);
if (qurry(1,1,n,now,now)!=0) maxx=i,zuo=now;
//可能已经被选了(被向右扩的结果 ,不能用s.count的原因是这个点不一定被扎死口,可能后边的还向前连
//被扎死口的一定是0,0的不一定被扎死口
if (!s.count(now+1)) //右边的已经被选过了
{
if (maxx1]) maxx=a[now+1],zuo=now+1;
}
if (wz+1<=now-1)
{
int z=qurry(1,1,n,wz+1,now-1);
if (maxxif (zuo==now)
{
s.insert(now);change(1,1,n,now,0);
ans[i]=i;
continue;
}
if (zuo==now+1)
{
change(1,1,n,now+1,0);
continue;
}
bl(zuo,now);
}
for (int i=1;i<=n;i++) printf("%d ",ans[i]);
}