1 条题解

  • 0
    @ 2026-9-26 16:45:31

    题目

    双栈排序 O(nlog⁡n)O(n\log n) 版,O(n2)O(n^2) 可过弱化版P1155。

    分析

    经过长时间的手玩数据,可以发现某些点不可能在同一个栈中,考虑总结一个规律。

    对于下标 i,j(1≤i<j≤n)i,j(1\le i<j\le n),若 ai>aja_i>a_j,由于栈是后进先出,i,ji,j 之间没有任何限制;若 ai<aja_i<a_j,则必然要在 jj 进入之前弹出 ii,如果此时 jj 后面有 ak<ai<aj(i<j<k)a_k<a_i<a_j(i<j<k),ii 和 jj 的进出栈顺序发生了矛盾,那么 ii 和 jj 一定不能放在一个栈中。

    形式化地,对于 i<j<ki<j<k,若 ak<ai<aja_k<a_i<a_j,则 ii 和 jj 在不同的栈内。

    这一限制可以通过二分图体现出来,结合染色法判定,给未确定的点赋予较小的栈号即可在 O(n2+n+m)O(n^2+n+m) 时间内得出答案。

    很明显无法通过,于是考虑优化。

    优化建图

    倒序枚举每个 jj,维护后缀最小值 mnmn,值域在 [mn+1,j−1][mn+1,j-1] 之间且还没有枚举到的点与 jj 连边。

    区间连边可以使用线段树优化建图,分为入树和出树彼此连边即可。

    重点考虑如何只连未枚举到的点。每枚举到一个点,就把线段树上连接对应叶子节点的边删去。实现上,由于之前连的边使用了旧节点,所以应当把根到该叶子节点上的所有点更换成新的点。

    关于最终的图上的结点个数,可以简单算出应为 6.8×1066.8\times10^6 左右,可能会稍卡空间,应当避免使用邻接表存图,链式前向星消耗更少。

    建图上可以如图示建法,可以方便判断叶节点且减少点数。

    DFS 的细节

    与一般的染色法判定不同,对于线段树上的辅助点,是不能有一个具体的颜色的,因为可能有很多路径需要经过它。

    最初的想法是记录当前访问的点 uu、上一个经过的叶子节点 lastlast,是否访问的数组 visvis。对于下一步要走的点 vv,如果是叶子节点,就用 lastlast 和 vv 比较。反之,由于不知道 vv 之后会走到哪里,所以不管有没有访问过,都继续搜索 vv。

    很明显,时间直接爆炸,考虑对非叶节点 vv 做出限制。

    可以发现,如果搜完了点 vv,那么对于当前的 lastlast,经过点 vv 可以访问的所有叶节点的颜色都与 lastlast 不同,那么相当于只有颜色与 lastlast 相同的可以满足二分图,不同的一定不行。可以把这个颜色记录在 vv 上。就可以保证每个点只经过一次。

    #include<bits/stdc++.h>
    #pragma GCC optimize(2)
    using namespace std;
    const int N(1e5+10),M(6e6+8e5+10);
    int n,a[N],tot,h[M],ne[M<<1],e[M<<1],idx;
    bitset<M>vis,co,fg;
    struct node{
    	int l,r,ii,oo;
    }tr[N<<2];
    inline void add(int u,int v){
    	ne[++idx]=h[u],h[u]=idx,e[idx]=v;
    }
    inline void pushup(int u){
    	add(tr[u].ii,tr[u<<1].ii),add(tr[u].ii,tr[u<<1|1].ii);
    	add(tr[u<<1].oo,tr[u].oo),add(tr[u<<1|1].oo,tr[u].oo);
    }
    inline void build(int u,int l,int r){
    	tr[u]=/**/{l,r};
    	if(l==r){
    		tr[u].ii=tr[u].oo=l;
    		return ;
    	}
    	tr[u].ii=++tot;tr[u].oo=++tot;
    	int mid=l+r>>1;
    	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
    	pushup(u);
    }
    inline void mdf(int u,int l,int r,int x){
    	if(l<=tr[u].l&&tr[u].r<=r){
    		if(tr[u].l!=tr[u].r||!fg[tr[u].l])
    			add(x,tr[u].ii),add(tr[u].oo,x);
    		return ;
    	}
    	int mid=tr[u].l+tr[u].r>>1;
    	if(l<=mid) mdf(u<<1,l,r,x);
    	if(r>mid) mdf(u<<1|1,l,r,x);
    }
    inline void upd(int u,int x){
    	if(tr[u].l==tr[u].r) return ;
    	int mid=tr[u].l+tr[u].r>>1;
    	tr[u].ii=++tot;tr[u].oo=++tot;
    	upd(x<=mid?u<<1:u<<1|1,x);
    	if(tr[u<<1].l!=tr[u<<1].r||!fg[tr[u<<1].l])
    		add(tr[u].ii,tr[u<<1].ii),add(tr[u<<1].oo,tr[u].oo);
    	if(tr[u<<1|1].l!=tr[u<<1|1].r||!fg[tr[u<<1|1].l])
    		add(tr[u].ii,tr[u<<1|1].ii),add(tr[u<<1|1].oo,tr[u].oo);
    }
    inline void dfs(int u,int la){
    	vis[u]=1;
    	if(u>n) co[u]=co[la];
    	for(int i=h[u],v;i;i=ne[i]){
    		v=e[i];
    		if(v<=n){
    			if(!vis[v]){
    				co[v]=co[la]^1;
    				dfs(v,v);
    			}
    			else if(la!=v&&co[v]==co[la]){
    				puts("NIE");
    				exit(0);
    			}
    		}
    		else{
    			if(!vis[v]){
    				co[v]=co[la];
    				dfs(v,la);
    			}
    			else if(fg[v]&&co[u]!=co[v]){
    				puts("NIE");
    				exit(0);
    			}
    		}
    		if(u>n) fg[u]=fg[u]|fg[v];
    	}
    }
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i) scanf("%d",&a[i]);
    	tot=n;
    	build(1,1,n);
    	for(int i=n,mn=2e9;i>=1;--i){
    		if(a[i]<mn) mn=a[i];
    		else if(mn<a[i]-1) mdf(1,mn+1,a[i]-1,a[i]);
    		fg[a[i]]=1;
    		upd(1,a[i]);
    	}
    	for(int i=1;i<=n;++i)
    		if(!vis[a[i]]) dfs(a[i],a[i]);
    	puts("TAK");
    	for(int i=1;i<=n;++i) printf("%d ",co[a[i]]+1);
    	return 0;
    }
    
    • 1

    信息

    ID
    3745
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者