Description
你有一个长度为n 的排列P 与一个正整数K
你可以进行如下操作若干次使得排列的字典序尽量小
对于两个满足|i-j|>=K 且|Pi-Pj| = 1 的下标i 与j,交换Pi 与Pj
Input
第一行包括两个正整数n 与K
第二行包括n 个正整数,第i 个正整数表示Pi
Output
输出一个新排列表示答案
输出共n 行,第i 行表示Pi
Sample Input
8 3
4 5 7 8 3 1 2 6
Sample Output
1
2
6
7
5
3
4
8
Data Constraint
对于前20% 的数据满足n <= 6
对于前50% 的数据满足n <= 2000
对于100% 的数据满足n <= 500000
Solution
读入 p[i] ,转化为: q[p[i]]=i , q[i] 表示值为 i 的位置在哪。
考虑对于
q[i] 和 q[j] ,若 |q[i]−q[j]|<k (相距小于 k ),则q[i] 和 q[j] 始终不能互换位置。于是 q[i] 向 q[j] (i>j) 连一条有向边即可,这样就构成了一个有向无环图。
将入度为 0 的点加入一个小根堆,做一遍拓扑排序。
倒着枚举 i ,每次从堆里取出一个编号最小的
x ,使 p[i]=x ,再将 x 连出的点入度减一,为 0 则加入堆中。
最后使
q[p[i]]=i , 再将 q 输出即可。为什么要维护一个堆呢?因为如果两点连了边(不能位置互换),就不会同时存在于堆中。
在队中的点,肯定是可以位置互换的,那肯定是将小的数填在位置小那里,才能保证字典序最小。
-但是这种连边时间复杂度达到了
由于一个点只需与离它最近的点连边,维护一个维护最小值的线段树,
倒着枚举 i ,每次查询
[q[i]−k+1,q[i]] 和 [q[i],q[i]+k−1] 两个区间的最小值即距离最近的两个点(一大一小),连边后再将 q[i] 这个位置单点修改成编号 i 即可。
这样同样做一次拓扑排序,不会影响正确性,时间复杂度就成了
O(N log N) 。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=5e5+1;
int tot;
int a[N],b[N],f[N<<2];
int first[N],next[N<<2],en[N<<2],d[N];
bool bz[N];
priority_queue<int,vector<int>,less<int> >q;
inline int read()
{
int X=0,w=1; char ch=0;
while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();}
while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar();
return X*w;
}
inline void insert(int x,int y)
{
next[++tot]=first[x];
first[x]=tot;
en[tot]=y;
d[y]++;
}
inline void change(int v,int l,int r,int x,int y)
{
if(l==r)
{
f[v]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid) change(v<<1,l,mid,x,y); else change(v<<1|1,mid+1,r,x,y);
f[v]=min(f[v<<1],f[v<<1|1]);
}
inline int find(int v,int l,int r,int x,int y)
{
if(x<=l && r<=y) return f[v];
int mid=(l+r)>>1;
if(y<=mid) return find(v<<1,l,mid,x,y);
if(x>mid) return find(v<<1|1,mid+1,r,x,y);
return min(find(v<<1,l,mid,x,mid),find(v<<1|1,mid+1,r,mid+1,y));
}
int main()
{
int n=read(),k=read();
for(int i=1;i<=n;i++) b[a[i]=read()]=i;
memset(f,60,sizeof(f));
for(int i=n;i;i--)
{
int x=find(1,1,n,b[i]-k+1,b[i]);
if(x<=n) insert(b[x],b[i]);
x=find(1,1,n,b[i],b[i]+k-1);
if(x<=n) insert(b[x],b[i]);
change(1,1,n,b[i],i);
}
for(int i=1;i<=n;i++)
if(!d[i]) q.push(i);
for(int i=n;i;i--)
{
a[i]=q.top();
q.pop();
for(int j=first[a[i]];j;j=next[j])
if(!--d[en[j]]) q.push(en[j]);
}
for(int i=1;i<=n;i++) b[a[i]]=i;
for(int i=1;i<=n;i++) printf("%d\n",b[i]);
return 0;
}