Poi2010 Railway

来源:互联网 发布:windows api 窗口置顶 编辑:程序博客网 时间:2024/04/27 18:30

Task:

A railroad siding consists of two (dead-end) sidetracks 1 and 2. The siding is entered by track A, and left by track B (see figure below).
这里写图片描述

There are cars on track A, numbered from to . They are arranged in such a way that they enter the siding in the order . The cars are to be transferred to the siding, so that they leave it by track B in the order . Each car is to be transferred once from track A to one of the sidetracks 1 or 2, and later (possibly after some transfers of the remaining cars) once from that sidetrack to the track B. The sidetracks are long enough to store even the longest trains, so there is no need to worry about their capacity.

Input
The first line of the standard input holds one integer () that denotes the number of cars for transfer. The second line stores the numbers that are a permutation of (i.e., each belongs to , and all these numbers are unique), separated by single spaces.

Output
The first line of the standard output should contain the word TAK (yes in Polish) if there is a way of transferring the cars so that they enter track B in the order , or the word NIE (no in Polish) if it is impossible. If the answer is TAK, the second line should give, separated by single spaces, the numbers of sidetracks (1 or 2) to which successive cars are moved in a correct transfer. If there are several ways of making the transfer, choose one arbitrarily.

Sample Input
4
1 3 4 2

4
2 3 4 1

Sample Output
TAK
1 1 2 1
NIE

Solution:

首先看到这题一定能想到NOIP2008的那道双栈排序吧……

那么,参考双栈排序,我们就能知道,对于这样一对i,j,如果i<j,a[i]<a[j],我们不妨称之为前向边.

这种边相比于之后要说的后向边更加难求,我们现在会有一个x,要去求u,我们考虑所有的点,它只可能在(u,l(u)]的区间内能够被选取,所以我们用线段树来维护它,对于每个u,我们把它的值放进(u,l(u)]这个区间里,线段树的每个节点放一个链表,每次查询出一个值后,就在线段树上把这些点都删去,而这样的复杂度最多会是O(logn)的(加入O(logn),删除O(logn)),然后我们去保证这个链表内的数呈单调递增,这样能保证查询的均摊复杂度为O(logn)(不会再需要去遍历一遍链表以查询满足的节点编号).

这样,我们就能把前向边求出来了.

接下来是之前说的后向边,就是满足x< u<=l(x),a[u]>a[x]

这样的边就被我们叫做后向边.而后向边相对前向边就好求很多(刚才是不是说过这句话了),我们只需要用线段树来维护一下这个,如果一个点已经被取过,我们就把它的值赋为无限小,那么它就一定不会出现在以后的查询中了,如果有,那就证明这段区间已经没有没被取过的点了,那么我们就得到了这个点所有的后向边,停止枚举就好了.

#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>#include<vector>#define M 100005#define INF 1000000007using namespace std;int a[M],Appear[M],ap[M],Co[M];int n;bool mark[M];struct Mx_Seg_Tree{    int Mx[M<<2],v[M];    int up(int A,int B){        if(v[A]<v[B])return B;        else return A;    }    void build(int L,int R,int p){        if(L==R){            Mx[p]=L;            return;        }        int mid=L+R>>1;        build(L,mid,p<<1),build(mid+1,R,p<<1|1);        Mx[p]=up(Mx[p<<1],Mx[p<<1|1]);    }    void update(int L,int R,int p,int x,int val){        if(L==R){            v[L]=val;            return;        }        int mid=L+R>>1;        if(mid>=x)update(L,mid,p<<1,x,val);        else update(mid+1,R,p<<1|1,x,val);        Mx[p]=up(Mx[p<<1],Mx[p<<1|1]);    }    int query(int L,int R,int p,int l,int r){        if(L==l&&R==r)return Mx[p];        int mid=L+R>>1;        if(mid>=r)return query(L,mid,p<<1,l,r);        else if(mid<l)return query(mid+1,R,p<<1|1,l,r);        return up(query(L,mid,p<<1,l,mid),query(mid+1,R,p<<1|1,mid+1,r));    }}Mx;struct W{    int fr,to,v;}lis[M<<5];int tot=0;struct Mi_SegTree{    int Begin[M<<2],End[M<<2];    void build(int L,int R,int p){        Begin[p]=End[p]=-1;        if(L==R)return;        int mid=L+R>>1;        build(L,mid,p<<1);        build(mid+1,R,p<<1|1);    }    void update(int L,int R,int p,int l,int r,int v){        if(L==l&&R==r){            lis[tot].fr=End[p];            lis[tot].v=v;            lis[tot].to=-1;            if(Begin[p]==-1)Begin[p]=tot;            if(End[p]!=-1)lis[End[p]].to=tot;            End[p]=tot++;            return;        }        int mid=L+R>>1;        if(mid>=r)update(L,mid,p<<1,l,r,v);        else if(mid<l)update(mid+1,R,p<<1|1,l,r,v);        else update(L,mid,p<<1,l,mid,v),update(mid+1,R,p<<1|1,mid+1,r,v);    }    int query(int L,int R,int p,int x){        for(int i=Begin[p];i!=-1;i=lis[i].to){            if(mark[Co[lis[i].v]]){//要删掉这个点                 if(i==Begin[p]&&i==End[p])Begin[p]=End[p]=-1;                else if(i==Begin[p])Begin[p]=lis[i].to;                else if(i==End[p])End[p]=lis[i].fr;                else {                    int fr=lis[i].fr,to=lis[i].to;                    lis[fr].to=to,lis[to].fr=fr;                }            }else if(lis[i].v>=a[x])break;            else return lis[i].v;        }        if(L==R)return -1;        int mid=L+R>>1;        if(mid>=x)return query(L,mid,p<<1,x);        else return query(mid+1,R,p<<1|1,x);    }}Mi;int col[M];vector<int>edge[M];void build_edge(int x){    //front     mark[x]=true;    bool f=true;    if(x>1){        int now;        while((now=Mi.query(1,n,1,x))!=-1){            edge[x].push_back(Co[now]);            edge[Co[now]].push_back(x);            build_edge(Co[now]);        }    }    //behind     if(x+1>ap[a[x]])return;    f=true;    while(f){        int now=Mx.query(1,n,1,x+1,ap[a[x]]);        if(Mx.v[now]==-INF||Mx.v[now]<a[x]){            f=false;        }else if(mark[now]){            Mx.update(1,n,1,now,-INF);        }else if(Mx.v[now]>a[x]){            edge[x].push_back(now);            edge[now].push_back(x);            build_edge(now);            Mx.update(1,n,1,now,-INF);        }    }}void Color(int x){    int up=edge[x].size();    for(int i=0;i<up;i++){        int to=edge[x][i];        if(col[to]!=0)continue;        if(col[x]==1)col[to]=2;        else col[to]=1;        Color(to);    }}struct Stk{    int sz,stk[M];    void input(int x){        stk[sz++]=x;    }    int top(){        if(sz==0)return -1;        return stk[sz-1];    }    void pop(){        sz--;    }}stk1,stk2;int main(){    scanf("%d",&n);    a[n+1]=INF;    for(int i=1;i<=n;i++)scanf("%d",&a[i]),ap[a[i]]=i,Co[a[i]]=i;    for(int i=2;i<=n;i++)ap[i]=max(ap[i-1],ap[i]);    Mi.build(1,n,1);    for(int i=1;i<=n;i++)if(ap[i]>Co[i])Mi.update(1,n,1,Co[i]+1,ap[i],i);    for(int i=1;i<=n;i++)Mx.v[i]=a[i];    Mx.build(1,n,1);    memset(mark,0,sizeof(mark));    for(int i=1;i<=n;i++)        if(!mark[i])build_edge(i);    for(int i=1;i<=n;i++)        if(col[i]==0){            col[i]=1;            Color(i);        }    int ned=1;    for(int i=1;i<=n;i++){        while(stk1.top()==ned||stk2.top()==ned){            if(stk1.top()==ned)stk1.pop();            else stk2.pop();            ned++;        }        if(col[i]==1)stk1.input(a[i]);        else stk2.input(a[i]);    }    while(stk1.top()==ned||stk2.top()==ned){        if(stk1.top()==ned)stk1.pop();        else stk2.pop();        ned++;    }    if(ned==n+1){        puts("TAK");        bool f=false;        for(int i=1;i<=n;i++){            if(f)putchar(' ');f=true;            printf("%d",col[i]);        }        puts("");    }else puts("NIE");    return 0;}
1 0