AGC001 F - Wide Swap【线段树+堆+拓扑排序】
给出的模型很难搞,所以转换一下,记p[i]为i这个数的位置,然后相邻两个p值差>k的能交换,发现使原问题字典序最小也需要使这里的字典序最小
注意到p值差<=k的前后顺序一定不変,那么可以n^2建图用堆跑最小字典序拓扑序
考虑优化,每个点需要向[p[i]-k+1,p[i]+k-1]这段区间的数连边,但是有一些边是多余的,也就是区间[p[i]-k+1,p[i]],[p[i],p[i]+k-1]这两个区间内的数一定两两有边所以连向当前点后面最近的一个即可,这个用线段树来找
#include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int N=3000005;
int n,k,a[N],p[N],h[N],cnt,d[N],tot;
struct xds
{
int l,r,p;
}t[N];
struct qwe
{
int ne,to;
}e[N];
int read()
{
int r=0,f=1;
char p=getchar();
while(p>'9'||p<'0')
{
if(p=='-')
f=-1;
p=getchar();
}
while(p>='0'&&p<='9')
{
r=r*10+p-48;
p=getchar();
}
return r*f;
}
void build(int ro,int l,int r)
{
t[ro].l=l,t[ro].r=r,t[ro].p=n+1;
if(l==r)
return;
int mid=(l+r)>>1;
build(ro<<1,l,mid);
build(ro<<1|1,mid+1,r);
}
void update(int ro,int p,int v)
{
if(t[ro].l==t[ro].r)
{
t[ro].p=v;
return;
}
int mid=(t[ro].l+t[ro].r)>>1;
if(p<=mid)
update(ro<<1,p,v);
else
update(ro<<1|1,p,v);
t[ro].p=min(t[ro<<1].p,t[ro<<1|1].p);
}
int ques(int ro,int l,int r)
{
if(t[ro].l==l&&t[ro].r==r)
return t[ro].p;
int mid=(t[ro].l+t[ro].r)>>1;
if(r<=mid)
return ques(ro<<1,l,r);
else if(l>mid)
return ques(ro<<1|1,l,r);
else
return min(ques(ro<<1,l,mid),ques(ro<<1|1,mid+1,r));
}
void add(int u,int v)
{//cerr<<u<<" "<<v<<endl;
cnt++;
e[cnt].ne=h[u];
e[cnt].to=v;
d[v]++;
h[u]=cnt;
}
int main()
{
n=read(),k=read();
for(int i=1;i<=n;i++)
a[i]=read(),p[a[i]]=i;
build(1,1,n);
for(int i=n;i>=1;i--)
{
int x=ques(1,p[i],min(n,p[i]+k-1)),y=ques(1,max(1,p[i]-k+1),p[i]);
if(x<=n)
add(p[i],p[x]);
if(y<=n)
add(p[i],p[y]);
update(1,p[i],i);
}
priority_queue<int,vector<int>,greater<int> >q;
for(int i=1;i<=n;i++)
if(!d[i])
q.push(i);
while(!q.empty())
{
int u=q.top();
p[++tot]=u;
q.pop();
for(int i=h[u];i;i=e[i].ne)
if(!(--d[e[i].to]))
q.push(e[i].to);
}
for(int i=1;i<=n;i++)
a[p[i]]=i;
for(int i=1;i<=n;i++)
printf("%d\n",a[i]);
return 0;
}