BZOJ
数据范围为 1 0 5 10^5 105的双栈排序。
为了方便讲述,令 L [ i ] L[i] L[i]表示比 a i a_i ai小的最靠右的位置。那么对于所有的 i < j < L [ i ] i<j<L[i] i<j<L[i]且 a j > a i a_j>a_i aj>ai,它们显然都不能放在同一个栈中。
仍然可以沿用弱化版的二分图模型,我们把不能放在一起的连一条边,然后看能不能二分图染色。但这里我们会发现边数可能达到 O ( n 2 ) O(n^2) O(n2)级别,那么不妨思考构造这个二分图的一棵生成树,然后再检验是否为二分图。不妨建dfs树,由于不能存下来,那么我们就只关心如何获得一条合法的边。
对于 i i i,有下面这么两种情况, ( i , j ) (i,j) (i,j)会有边
对于第一种边,可以用线段树维护区间最大值,查询最大值是否大于 a i a_i ai。这个也不难支持删除操作。
对于第二种边,线段树在每个节点上维护链表,先对于所有的 j j j,都把它插到线段树的各个节点对应的链表中。那么查询相当于是单点查询,贪心地每次查询链表的最小值。我们可以在建树时,把链表按顺序插入,以保证链顶是最小的,这样查询的复杂度就是 O ( log n ) O(\log n) O(logn)了。
为了保证防止dfs重复访问节点,我们需要删除已经访问过的点的贡献。第一条边不难删除。第二条边,由于每次插入都是连续的,那么可以记录下插入的区间,这样就可以支持删除了。
综上,时间、空间复杂度均为 O ( n log n ) O(n\log n) O(nlogn)。
事实上,波兰人还表示:k栈排序就是图的k染色问题。
#include
#include
using namespace std;
typedef long long ll;
const int maxn=100010,INF=0x3f3f3f3f;
template <typename Tp> inline int getmin(Tp &x,Tp y){return y<x?x=y,1:0;}
template <typename Tp> inline int getmax(Tp &x,Tp y){return y>x?x=y,1:0;}
template <typename Tp> inline void read(Tp &x)
{
x=0;int f=0;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
if(ch=='-') f=1,ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
if(f) x=-x;
}
struct data{int v,pre,nxt;}edge[maxn*40];
struct node{
int mx,id;
node(const int _mx=0,const int _id=0){mx=_mx;id=_id;}
node operator + (const node &b)const{return mx>b.mx?(*this):b;}
}t[maxn<<2];
int n,p,a[maxn],pos[maxn],mx[maxn],L[maxn],cor[maxn],S[maxn],T[maxn];
int stk[3][maxn],head[maxn<<2];
void insert(int u,int v)
{
edge[head[u]].pre=++p;
edge[p]=(data){v,0,head[u]};head[u]=p;
}
void build(int l,int r,int rt)
{
if(l==r)
{
t[rt]=node(a[l],l);
return ;
}
int m=(l+r)>>1;
build(l,m,rt<<1);build(m+1,r,rt<<1|1);
t[rt]=t[rt<<1]+t[rt<<1|1];
}
void update1(int l,int r,int pos,int rt)
{
if(l==r){t[rt]=node(0,0);return ;}
int m=(l+r)>>1;
if(pos<=m) update1(l,m,pos,rt<<1);
if(m<pos) update1(m+1,r,pos,rt<<1|1);
t[rt]=t[rt<<1]+t[rt<<1|1];
}
node query1(int l,int r,int L,int R,int rt)
{
if(L<=l&&r<=R) return t[rt];
int m=(l+r)>>1;node res(0,0);
if(L<=m) res=res+query1(l,m,L,R,rt<<1);
if(m<R) res=res+query1(m+1,r,L,R,rt<<1|1);
return res;
}
void update2(int l,int r,int L,int R,int val,int rt)
{
if(L<=l&&r<=R){insert(rt,val);return ;}
int m=(l+r)>>1;
if(L<=m) update2(l,m,L,R,val,rt<<1);
if(m<R) update2(m+1,r,L,R,val,rt<<1|1);
}
int query2(int l,int r,int p,int val,int rt)
{
if(head[rt]&&edge[head[rt]].v<val)
{
int res=pos[edge[head[rt]].v];
head[rt]=edge[head[rt]].nxt;
return res;
}
if(l==r) return -1;
int m=(l+r)>>1;
if(p<=m) return query2(l,m,p,val,rt<<1);
if(m<p) return query2(m+1,r,p,val,rt<<1|1);
}
int getnxt(int x)
{
node tmp=query1(1,n,x,L[x],1);
if(tmp.mx>a[x]) return tmp.id;
return query2(1,n,x,a[x],1);
}
void del(int x)
{
int a,b;update1(1,n,x,1);
for(int i=S[x];i<=T[x];i++)
{
a=edge[i].pre;b=edge[i].nxt;
edge[a].nxt=b;edge[b].pre=a;edge[i].v=INF;
}
}
void dfs(int x,int c)
{
cor[x]=c;del(x);
for(int v=getnxt(x);~v;v=getnxt(x)) dfs(v,3-c);
}
void input()
{
read(n);
for(int i=1;i<=n;i++){read(a[i]);pos[a[i]]=i;}
for(int i=1;i<=n;i++) mx[i]=max(mx[i-1],pos[i]);
for(int i=1;i<=n;i++) L[pos[i]]=mx[i];
build(1,n,1);
for(int i=n,j;i;i--)
{
j=pos[i];S[j]=p+1;
update2(1,n,j,L[j],i,1);T[j]=p;
}
}
void work()
{
int p,f,tot=1,tp[3];tp[1]=tp[2]=0;
for(int i=1;i<=n;i++) if(!cor[i]) dfs(i,1);
for(int i=1;i<=n;i++)
{
p=cor[i];
stk[p][++tp[p]]=a[i];
do{
f=0;
while(stk[p][tp[p]]==tot) ++tot,--tp[p],f=1;
while(stk[3-p][tp[3-p]]==tot) ++tot,--tp[3-p],f=1;
}while(f);
}
if(tot<=n){puts("NIE");return ;}
puts("TAK");
for(int i=1;i<=n;i++) printf("%d ",cor[i]);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
input();
work();
return 0;
}