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;
}
posted @ 2021-11-11 18:57  曙诚  阅读(41)  评论(0)    收藏  举报