AT1984 [AGC001F] Wide Swap(特殊處理+歸併排序)
阿新 • • 發佈:2021-11-11
題意
給定一個 \(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\) 中的下標和數值)。那麼原排列中的限制條件在新的排列中就表現為,只能交換兩個相鄰
注意到此時 \(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\)
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; }