题意:
你有一个排列,长度为N。然后将i和j两个位置的数字交换的条件是:|i-j|>=K并且|Ai-Aj|=1.
然后你可以进行无数次交换,输出操作后能够得到的最小的字典序的排列。
数据范围:
N<=500000.
思路:
这道题在考场上是真的没做出来…
那就直接说正解了。假设原排列是P,那么我们在定义一个数组是Q,满足Q[P[i]]=i(感觉像是反函数)。然后目的P的字典序最小,就是Q的字典序最小。
这个其实很好想。也就是让Q中的‘1’尽量靠前,因为这样子值越小的数字就越靠前了。
那么问题就这样子转换过来了,现在我们可以完全忘记P数组了,只管Q数组。
那么原问题的交换条件就变成了:
Q
i
Q_i
Qi,
Q
i
+
1
Q_i+1
Qi+1可以进行交换,当且仅当
∣
Q
i
−
Q
i
+
1
∣
>
=
K
|Q_i-Q_{i+1}|>=K
∣Qi−Qi+1∣>=K。那么利用一下冒泡排序的思想,就有:当
∣
Q
i
−
Q
j
∣
<
K
|Q_i-Q_j|<K
∣Qi−Qj∣<K的时候,
Q
i
Q_i
Qi和
Q
j
Q_j
Qj永远都不会改变相对位置,那么就是说Qj永远都会在Qi的后面。到了这里就可以看出拓扑序的影子了。满足
∣
Q
i
−
Q
j
∣
<
=
K
−
1
|Q_i-Q_j|<=K-1
∣Qi−Qj∣<=K−1的时候,就让
Q
i
Q_i
Qi连一条边到
Q
j
Q_j
Qj,最后跑一边拓扑排序就可以了。
但是这样是有问题的。因为这样子建边是O(n^2)的,炸的惨裂。因为当x->y,y->z时,x->z这条边就是没有用的。那么对于每一个点,我们只需要找到
Q
i
−
Q
j
<
=
K
−
1
Q_i-Q_j<=K-1
Qi−Qj<=K−1的最近的那个点和
Q
j
−
Q
i
<
=
K
−
1
Q_j-Q_i<=K-1
Qj−Qi<=K−1的最近的那个点,然后连两条边,就能够保证
Q
i
Q_i
Qi的相对位置了。这样子时间和空间复杂度就大大降低了。
还有一点就是拓扑排序需要使用优先队列来保证字典序最小,这还是比较好想的。
然后最后转换回来的时候,就是
A
a
n
s
i
=
i
A_{ans_i}=i
Aansi=i(ans是Q的字典序最小的数组)
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define MAXN 500000
using namespace std;
priority_queue<int,vector<int>,greater<int> > que;
int N,K;
int p[MAXN+5],q[MAXN+5];
int tree[MAXN*4],d[MAXN+5];
vector<int> G[MAXN+5],ans;
void Build(int i,int l,int r)
{
tree[i]=0;
if(l==r)
return;
int mid=(l+r)/2;
Build(i*2,l,mid);
Build(i*2+1,mid+1,r);
}
void PushUp(int i)
{
tree[i]=max(tree[i*2],tree[i*2+1]);
}
void Insert(int i,int l,int r,int p,int val)
{
if(l==r)
{
tree[i]=val;
return;
}
int mid=(l+r)/2;
if(p<=mid)
Insert(i*2,l,mid,p,val);
else
Insert(i*2+1,mid+1,r,p,val);
PushUp(i);
}
int Query(int i,int l,int r,int ql,int qr)
{
if(qr<l||ql>r)
return 0;
if(ql<=l&&r<=qr)
return tree[i];
int mid=(l+r)/2;
return max(Query(i*2,l,mid,ql,qr),Query(i*2+1,mid+1,r,ql,qr));
}
void BFS()
{
for(int i=1;i<=N;i++)
if(d[i]==0)
que.push(i);
while(que.empty()==false)
{
int u=que.top();
que.pop();
ans.push_back(u);
for(int i=0;i<(int)G[u].size();i++)
{
int v=G[u][i];
d[v]--;
if(d[v]==0)
que.push(v);
}
}
}
int main()
{
// freopen("swap.in","r",stdin);
// freopen("swap.out","w",stdout);
scanf("%d %d",&N,&K);
for(int i=1;i<=N;i++)
{
scanf("%d",&p[i]);
q[p[i]]=i;
}
Build(1,1,N);
for(int i=1;i<=N;i++)
{
int pos=Query(1,1,N,max(q[i]-K+1,1),q[i]);
if(pos!=0)
{
G[q[pos]].push_back(q[i]);
d[q[i]]++;
}
pos=Query(1,1,N,q[i],min(N,q[i]+K-1));
if(pos!=0)
{
G[q[pos]].push_back(q[i]);
d[q[i]]++;
}
Insert(1,1,N,q[i],i);
}
for(int i=1;i<=N;i++)
sort(G[i].begin(),G[i].end());
BFS();
for(int i=0;i<(int)ans.size();i++)
p[ans[i]]=i+1;
for(int i=1;i<=N;i++)
printf("%d\n",p[i]);
return 0;
}