1. 程式人生 > >【Atcoder】ARC 080 E - Young Maids

【Atcoder】ARC 080 E - Young Maids

個數 amp -a urn rmq 遞歸 algorithm b- printf

【算法】數學+堆

【題意】給定n個數的排列,每次操作可以取兩個數按序排在新序列的頭部,求最小字典序。

【題解】

轉化為每次找字典序最小的兩個數按序排在尾部,則p1和p2的每次選擇都必須滿足:p1在當前序列的奇數位置,p2在當前序列的偶數位置且位於p1之後。滿足條件的情況下每次找最小。

每次找到p1和p2都把序列劃分為3部分,遞歸進行,初步想到使用歸並。

進一步考慮性質,每對數字要出現必須它的上屬序列的p1和p2必須出現,此外沒有其他要求。

所以用優先隊列維護每個序列,序列的優先級為p1,每次處理一個序列才能加入其三個子序列。

技術分享
#include<cstdio>
#include<algorithm>
#include
<queue> #include<cstring> using namespace std; const int maxn=200010,inf=0x3f3f3f3f; int a[maxn],n,logs[maxn],d[2][maxn][30],pos[maxn]; struct cyc{ int l,r,x; bool operator <(const cyc &a)const {return x>a.x;} }; priority_queue<cyc>q; void RMQ_INIT(int k){ for(int
j=1;(1<<j)<=n;j++) for(int i=1;i+(1<<j)-1<=n;i++) d[k][i][j]=min(d[k][i][j-1],d[k][i+(1<<(j-1))][j-1]); } int rmq(int l,int r,int k){ int K=logs[r-l+1]; return min(d[k][l][K],d[k][r-(1<<K)+1][K]); } int main(){ scanf("%d",&n);
for(int i=1;i<=n;i++){ scanf("%d",&a[i]); d[i&1][i][0]=a[i];//1奇數 d[(i&1)^1][i][0]=inf;//0偶數 pos[a[i]]=i;//因為是排列可以直接記位置,省去RMQ的位置記錄。 } logs[0]=-1;for(int i=1;i<=n;i++)logs[i]=logs[i>>1]+1; RMQ_INIT(0);RMQ_INIT(1); q.push((cyc){1,n,rmq(1,n,1)}); int L,R,p1,p2; while(!q.empty()){ cyc x=q.top();q.pop(); L=x.l;R=x.r;p1=pos[x.x]; p2=pos[rmq(p1+1,R,(p1&1)^1)]; printf("%d %d ",a[p1],a[p2]); if(L<p1)q.push((cyc){L,p1-1,rmq(L,p1-1,(L&1))}); if(p1<p2-1)q.push((cyc){p1+1,p2-1,rmq(p1+1,p2-1,(p1+1)&1)}); if(p2<R)q.push((cyc){p2+1,R,rmq(p2+1,R,(p2+1)&1)}); } return 0; }
View Code

【Atcoder】ARC 080 E - Young Maids