题目
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
对于100% 的数据满足n <= 500000
题解
在比赛的过程中,我发现p[i]变成x,当前仅当条件:x和p[i]之间的所有数,p[i]都与他们距离≥k。
为了方便解决此题,我们将问题转化为:q[p[i]]=i,然后对q序列解决问题。那么两个数能移动,当且仅当相邻且
|q[i]−q[i−1]|≥k
.
所以,我们只要找到与i相关的两个数j1,j2>i,
j1∈[q[i]−k+1,q[i]]且最小,j2∈[q[i],q[i]+k−1]且最小
。然后q[i]向q[j1],q[j2]各连一条边,
表示j1无论怎么只靠跟后面换,都不能够换到i前面(若j1可以跟前面换则不优)。j2同理
即表示,最后i表示的数在j1,j2前面。
这样子我们搞出了一个DAG。
由于题目要求输出最小字典序的序列,所以保证拓扑序字典序最小即可。
解决方案:用优先队列存储即可,每次让最小的出队就行了。
然而C++的priority_queue默认为“大根堆”。“小根堆”怎么办?
解决方案:将加进优先队列的数取相反数,然后取出来的时候再去相反数。
对这道题目的心得:
①不要将数列上的数看得太死,要让他们在脑中”动起来”。
②一道题目这么难,肯定有突破口,比如这题的一定交换相邻的两个数,这两个数绝对值等于1!!!所以可以想到一直交换,直到有一个交换不到的数就截止。
③最重要的一点,对于这种序列(这种序列是排列)的问题,如果涉及到要利用”每个数的位置”来解决问题,就可以试下考虑逆数组。即q[p[i]]=i。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define N 500010
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
int to,next;
};note edge[N*2];
int p[N],q[N];
int tr[N*20],ru[N];
int i,j,k,L,R,l,r,n,temp,now,ans,step;
int tot,head[N];
priority_queue<int>qu;
bool pp;
void read(int &n){
n=0;char ch=getchar();
while((ch<'0' || ch>'9') && ch!='-')ch=getchar();
int w=1;
if(ch=='-')w=-1,ch=getchar();
while('0'<=ch && ch<='9')n=n*10+ch-'0',ch=getchar();
n*=w;
}
void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
void lb(int x,int y){
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
ru[y]++;
}
void update(int ps){
tr[ps]=min(tr[ps*2],tr[ps*2+1]);
}
void change(int ps,int l,int r,int x,int z){
if(l==r){
tr[ps]=z;
return;
}
int wz=(l+r)/2;
if(x<=wz)change(ps*2,l,wz,x,z);
else change(ps*2+1,wz+1,r,x,z);
update(ps);
}
void find(int ps,int l,int r,int x,int y){
if(l==x && r==y){
temp=min(temp,tr[ps]);
return;
}
int wz=(l+r)/2;
if(y<=wz)find(ps*2,l,wz,x,y);else
if(x>wz)find(ps*2+1,wz+1,r,x,y);else{
find(ps*2,l,wz,x,wz);
find(ps*2+1,wz+1,r,wz+1,y);
}
}
int main(){
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
read(n),read(k);
fo(i,1,n)read(p[i]),q[p[i]]=i;
memset(tr,127,sizeof(tr));
fd(i,n,1){
L=max(1,q[i]-k+1);R=q[i];temp=n+1;
find(1,1,n,L,R);
if(temp<=n)lb(q[i],q[temp]);
L=q[i];R=min(n,q[i]+k-1);temp=n+1;
find(1,1,n,L,R);
if(temp<=n)lb(q[i],q[temp]);
change(1,1,n,q[i],i);
}
fo(i,1,n)if(!ru[i]) qu.push(-i);
memset(q,0,sizeof(q));
fo(i,1,n){
int now=qu.top();
now=-now;
q[i]=now;
qu.pop();
for(j=head[now];j;j=edge[j].next){
ru[edge[j].to]--;
if(!ru[edge[j].to])qu.push(-edge[j].to);
}
}
fo(i,1,n)p[q[i]]=i;
fo(i,1,n)write(p[i]),putchar('\n');
return 0;
}