AT1984 [AGC001F] Wide Swap(特殊处理+归并排序)
题意
给定一个 \(1 \sim n\) 的排列 \(P\),\(P_i\) 和 \(P_j\) (\(1 \leq i < j \leq n\))可以交换,当且仅当 \(i\) 和 \(j\) 满足 \(j-i \geq K(1 \leq K \leq n-1)\),且 \(|P_i-P_j|==1\)。求所有可能的排列中字典序最小的排列。
数据范围
\(2 \leq n \leq 500000\)。
思路
首先注意到题意中的 \(|P_i-P_j|==1\) 有些特殊。于是就可以考虑新定义一个排列 \(B\),满足 \(B_{P_i}=i\) (就是互换了 \(P\) 中的下标和数值)。那么原排列中的限制条件在新的排列中就表现为,只能交换两个相邻差值大于等于 \(K\) 的元素。
注意到此时 \(B_i=x\) 的含义是 \(P\) 中下标为 \(x\) 的数的值是 \(i\),那么要使 \(P\) 序列的字典序最小,就等价于使 \(B\) 的字典序最小。
考虑直接交换相邻的数,就类似于冒泡排序,可以用归并排序优化这个过程。对于当前的 \(B_i\) 和 \(B_j\),若 \(B_j\) 能转移到 \(B_i\) 前面,当且仅当 \(B_i \sim B_{mid}\) 中最小的元素 \(min_{B_i} \geq B_j+K\)。否则就只能移动到第一个满足 \(min_{B_i} \geq B_i+K\) 的数的前面。
最后再把 \(B\) 排列还原回 \(P\) 中,方法和起初 \(P\) 构造 \(B\) 的方法一样。时间复杂度 \(O(n \log n)\)。
code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e5+10;
const int INF=0x3f3f3f3f;
int K,minn[N],a[N],b[N],n,t[N];
void merge_sort(int l,int r)
{
if(l==r) return ;
int mid=l+r>>1,i=l,j=mid+1,k=l;
merge_sort(l,mid),merge_sort(mid+1,r);
minn[mid+1]=INF;for(int i=mid;i>=l;i--) minn[i]=min(minn[i+1],b[i]);
while(i<=mid&&j<=r)
if(minn[i]-b[j]>=K) t[k++]=b[j++];
else t[k++]=b[i++];
while(i<=mid) t[k++]=b[i++];
while(j<=r) t[k++]=b[j++];
for(int i=l;i<=r;i++) b[i]=t[i];
}
int main()
{
// freopen("AKnlc.in","r",stdin);
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]]=i;
merge_sort(1,n);
for(int i=1;i<=n;i++) a[b[i]]=i;
for(int i=1;i<=n;i++) printf("%d\n",a[i]);
return 0;
}

浙公网安备 33010602011771号