Description
你有一个长度为n 的排列P 与一个正整数K
你可以进行如下操作若干次使得排列的字典序尽量小
对于两个满足|i-j|>=K 且|Pi-Pj| = 1 的下标i 与j,交换Pi 与Pj对于100% 的数据满足n <= 500000
Solution
隔着K个数之间交换比较麻烦,不如变换一下题意
原序列记录每个位置上的数什么
不妨新开一个数组Q记录每个数在哪个位置。
交换只是相邻两个交换,只要相邻两个数的差的绝对值大于等于K就可以交换。
排列P的字典序最小,与数组Q的字典序最小是等价的。
有了这个结论,就可以直接在数组Q上做了。
下面的值都是数组Q上的值
显然,如果
|x−y|<K
,那么
x,y
的相对位置无论怎么换,x这个数总是在y前面的。
那么就有了一种 N2 的做法,对于每一对 |Q[i]−Q[j]|<K,i<j ,把 Q[i] 向 Q[j] 连边,构成一个DAG,那么构造出来的最小字典序的拓扑序就是最后换出来的Q,再还原成P即可。
最小字典序的拓扑序可以维护一个堆,每次取堆顶加入拓扑序,扩展节点,新走出的节点入度为0时加入堆即可。
然而可以发现,这样连边会有大量不必要的边。
对于
Q[x],Q[y],Q[z],x<y<z
,
如果
|Q[x]−Q[y]|<K,|Q[y]−Q[z]|<K,|Q[x]−Q[z]|<K
那么只需要Q[x]连Q[y],Q[y]连Q[z]即可。
那么从后往前扫,对于当前扫到的Q[i],只需要找到i后面最小的x,y,使得
Q[x]∈[Q[i]−K+1,Q[i]−1],Q[y]∈[Q[i]+1,Q[i]+K−1]
Q[i]分别向 Q[x],Q[y] 连边即可
这个东西可以维护一个权值线段树,从后向前扫的时候插入即可。
Code
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <cstdlib>
#include <queue>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
#define INF 2147483647
using namespace std;
int n,w[N],m,m1,n1,fs[N],nt[2*N],dt[2*N],rd[N],d[N],as[N],a1[N];
priority_queue <int> a;
void link(int x,int y)
{
nt[++m1]=fs[x];
rd[dt[fs[x]=m1]=y]++;
}
struct node
{
int s,l,r,s1;
}tr[5*N];
void up(int k)
{
tr[k].s=min(tr[tr[k].l].s,tr[tr[k].r].s);
tr[k].s1=max(tr[tr[k].l].s1,tr[tr[k].r].s1);
}
void ins(int k,int l,int r,int x,int v)
{
if(l==r) tr[k].s=v,tr[k].s1=v;
else
{
int mid=(l+r)/2;
if(!tr[k].l) tr[k].l=++n1,tr[n1].s=INF;
if(!tr[k].r) tr[k].r=++n1,tr[n1].s=INF;
if(x<=mid) ins(tr[k].l,l,mid,x,v);
else ins(tr[k].r,mid+1,r,x,v);
up(k);
}
}
int fd(int k,int l,int r,int x,int y)
{
x=max(x,l),y=min(y,r);
if(x>y||k==0) return INF;
if(l==x&&r==y) return tr[k].s;
int mid=(l+r)/2;
return min(fd(tr[k].l,l,mid,x,y),fd(tr[k].r,mid+1,r,x,y));
}
int fd2(int k,int l,int r,int x,int y)
{
x=max(x,l),y=min(y,r);
if(x>y||k==0) return 0;
if(l==x&&r==y) return tr[k].s1;
int mid=(l+r)/2;
return max(fd2(tr[k].l,l,mid,x,y),fd2(tr[k].r,mid+1,r,x,y));
}
void doit()
{
int l=0,r=d[0];
fo(i,1,n) if(rd[i]==0) a.push(-i);
while(r<n)
{
int k=-a.top();
a.pop();
d[++r]=k;
for(int i=fs[k];i;i=nt[i])
{
int p=dt[i];
rd[p]--;
if(rd[p]==0) a.push(-p);
}
}
}
int main()
{
cin>>n>>m;
n1=1;
fo(i,1,n)
{
scanf("%d",&a1[i]);
w[a1[i]]=i;
}
ins(1,1,n,w[n],n);
fod(i,n-1,1)
{
int p=fd(1,1,n,w[i],w[i]+m-1),q=fd(1,1,n,w[i]-m+1,w[i]);
if(p!=INF) link(w[i],w[p]);
if(q!=INF) link(w[i],w[q]);
ins(1,1,n,w[i],i);
}
doit();
fo(i,1,n) as[d[i]]=i;
fo(i,1,n) printf("%d\n",as[i]);
}