1. 程式人生 > >Luogu1155 NOIP2008雙棧排序(並查集)

Luogu1155 NOIP2008雙棧排序(並查集)

pan -s get 另一個 棧排序 ack stdin r+ def

  兩個位置i和j上的元素不能被放進同一個棧的充要條件顯然是存在k使i<j<k且ak<ai<aj。由此在保證合法的情況下貪心地放就是正確的了。

  至於如何判斷,可以記一下後綴最小值,每找到一對就利用補集並查集合並。放的時候要求與該棧所有元素不排斥且與另一個棧的元素不存在強制同棧的關系。

  怎麽感覺遠古時代noip都這麽困難啊。沒救了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include
<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<0||c>9) {if (c==-) f=-1;c=getchar();} while (c>=0&&c<=9) x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 1010 int n,a[N],mn[N],fa[N<<1],stk1[N],stk2[N],top1=0
,top2=0,tot=0,cur=0; char ans[N<<1]; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void merge(int x,int y){fa[find(x+n)]=find(y),fa[find(y+n)]=find(x);} int main() { #ifndef ONLINE_JUDGE freopen("stack.in","r",stdin); freopen("stack.out","w",stdout); const char LL[]="%I64d\n";
#else const char LL[]="%lld\n"; #endif n=read(); for (int i=1;i<=n;i++) a[i]=read(); mn[n+1]=n+1;for (int i=n;i;i--) mn[i]=min(a[i],mn[i+1]); for (int i=1;i<=n+n;i++) fa[i]=i; for (int i=1;i<=n;i++) for (int j=i+1;j<=n;j++) if (a[i]<a[j]&&mn[j+1]<a[i]) merge(i,j); for (int i=1;i<=n;i++) { while (a[stk1[top1]]==cur+1) cur++,ans[++tot]=b,top1--; bool flag=1; for (int j=1;j<=top1;j++) if (find(stk1[j]+n)==find(i)) {flag=0;break;} for (int j=1;j<=top2;j++) if (find(stk2[j])==find(i)) {flag=0;break;} if (flag) stk1[++top1]=i,ans[++tot]=a; else { while (a[stk1[top1]]==cur+1||a[stk2[top2]]==cur+1) if (a[stk1[top1]]==cur+1) cur++,ans[++tot]=b,top1--; else cur++,ans[++tot]=d,top2--; flag=1; for (int j=1;j<=top1;j++) if (find(stk1[j])==find(i)) {flag=0;break;} for (int j=1;j<=top2;j++) if (find(stk2[j]+n)==find(i)) {flag=0;break;} if (flag) stk2[++top2]=i,ans[++tot]=c; else {cout<<0;return 0;} } } while (top1||top2) if (top1&&a[stk1[top1]]<a[stk2[top2]]||top2==0) ans[++tot]=b,top1--; else ans[++tot]=d,top2--; for (int i=1;i<=tot;i++) cout<<ans[i]<< ; return 0; }

Luogu1155 NOIP2008雙棧排序(並查集)