BZOJ2080 POI2010 Railway

19 篇文章 0 订阅
6 篇文章 0 订阅

Problem

BZOJ

数据范围为 1 0 5 10^5 105的双栈排序。

Solution

为了方便讲述,令 L [ i ] L[i] L[i]表示比 a i a_i ai小的最靠右的位置。那么对于所有的 i &lt; j &lt; L [ i ] i&lt;j&lt;L[i] i<j<L[i] a j &gt; a i a_j&gt;a_i aj>ai,它们显然都不能放在同一个栈中。

仍然可以沿用弱化版的二分图模型,我们把不能放在一起的连一条边,然后看能不能二分图染色。但这里我们会发现边数可能达到 O ( n 2 ) O(n^2) O(n2)级别,那么不妨思考构造这个二分图的一棵生成树,然后再检验是否为二分图。不妨建dfs树,由于不能存下来,那么我们就只关心如何获得一条合法的边。

对于 i i i,有下面这么两种情况, ( i , j ) (i,j) (i,j)会有边

  • i &lt; j ≤ L [ i ] , a i &lt; a j i&lt;j\leq L[i],a_i&lt;a_j i<jL[i],ai<aj
  • j &lt; i ≤ L [ j ] , a j &lt; a i j&lt;i\leq L[j],a_j&lt;a_i j<iL[j],aj<ai

对于第一种边,可以用线段树维护区间最大值,查询最大值是否大于 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染色问题。

Code

#include <algorithm>
#include <cstdio>
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值