思路是用 deque 模拟,然后启发式合并。
lsti 表示第 i 块的上一个非空块,nxti 表示第 i 块的下一个非空块,empi=1 表示第 i 块为空。
调了整整三个小时,有 WA 有 TLE 有 MLE,实在受不了了。
#include<bits/stdc++.h>
#define ll long long
#define ri register int
#define back return
using namespace std;
ll read()
{
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
back x*f;
}
int n,sum,cnt,nxt[200005],emp[200005],f[200005],lst[200005];
deque<int> a[200005];
int main()
{
n=read();
int last=-1;
for(ri i=1;i<=n;i++)
{
int x=read();
if(x==last)
a[cnt].push_back(i);
else
a[++cnt].push_back(i),nxt[cnt-1]=cnt,lst[cnt]=cnt-1;
last=x;
}
int l=1,r=cnt;
while(sum<n)
{
while(emp[l])
l++;
while(emp[r])
r--;
for(ri i=l;i<=r;i++)
{
if(emp[i])
continue;
cout<<a[i].front()<<" ",sum++;
a[i].pop_front();
if(i==r)
cout<<"\n";
}
for(ri i=l;i<=r;i++)//启发式合并
{
if(emp[i]&&!f[i])
continue;
f[i]=0;//判断这个空块是不是刚刚才变空的
if(!a[i].size()||emp[i])
{
emp[i]=1;
if(i==l)
continue;
if(i==r)
break;
if(a[lst[i]].size()>a[nxt[i]].size())
{
for(ri j=0;j<a[nxt[i]].size();j++)
a[lst[i]].push_back(a[nxt[i]][j]);
nxt[lst[i]]=nxt[nxt[i]];
emp[nxt[i]]=1,f[nxt[i]]=1;
}
else
{
for(ri j=a[lst[i]].size()-1;j>=0;j--)
a[nxt[i]].push_front(a[lst[i]][j]);
lst[nxt[i]]=lst[lst[i]];
emp[lst[i]]=1,f[lst[i]]=1;
}
}
}
}
back 0;
}