BZOJ2080[Poi2010] Railway

30 篇文章 0 订阅
23 篇文章 1 订阅

BZOJ2080[Poi2010] Railway

Description

一个铁路包含两个侧线1和2,右边由A进入,左边由B出去。有n个车厢在通道A上,编号为1到n,它们被安排按照要求的顺序(a1,a2,a3,a4….an)进入侧线,进去还要出来,它们要按照编号顺序(1,2,3,4,5。。。。n)从通道B出去。他们从A到1或2,然后经过一系列转移从B出去,不用考虑容量问题。

题目描述

Input

输入:第一行一个整数n(1<=n<=100000)表示要转移的车厢总数,第二行为进入侧线的要求顺序a1.a2.a3.a4….an,由空格隔开。

Output

输出:如果可以按照编号顺序到通道B,则输出两行,第一行为TAK,第二行为n个由空格隔开的整数,表示每个车厢进入的侧线编号(1,2)。否则输出NIE。

Sample Input

[样例输入1]

4

1 3 4 2

[样例输入2]

4

2 3 4 1

Sample Output

[样例输出1]

TAK

1 1 2 1 (1号线进到侧线1,然后出来,3号进入侧线1,4号进入侧线2,2号进入侧线1,然后出来,接着3号出来,4号出来)

[样例输出2]

NIE (不可能。。No)

Solution:

这题就是NOIP2008双栈排序的加强版,时间复杂度被从 O(n2) 压缩到了 O(nlogn) 以内。这就是网上根本找不到题解的原因?

fin[i] 表示处理 i 的时候[1,fin[i]]的数字已经出栈了,也就是说这些数字都的下标在 i 的前面。由双栈排序的思想:如果出现A[k]<A[i]<A[j](i<j<k)的情况,则 i j互斥(不能放在同一个栈内)。于是我们可以得到:如果 fin[j]<A[i]<A[j](i<j) i j互斥。然而这表示的其实是一个区间,区间中的所有数与 j 互斥,也就是说区间内部的点染上的颜色是一样的。而这个区间我们就可以用并查集将它看成一个点(即这段区间最终在栈里是连续的一段)。

于是我们把这些权值的区间放到set里,每次对在[fin[i],data[i])区间的区间进行合并(使用影子并查集),如果发现并查集有冲突,那么就不可行,直接输出即可。否则我们把这些区间都用影子并查集合并,并把能合并的区间合并。最后直接模拟进出栈即可。

好好想了想好像复杂度并不能保证 O(nlogn) ,这取决于set中的元素个数。

注:由于 fin 数组单调递增,如果set的begin处小于 fin 就可以直接erase掉。这里有一个点:不存在跨过 fin 的区间。

因为set中的区间在栈中是连续的,如果 fin 值在区间中间,那么实际上 fin 值可以直接被推到这个区间的末尾。

证毕。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<set>
#include<vector>
#define M 100005
#define mp make_pair
#define _st ->first
#define _ed ->second
using namespace std;
int A[M],fin[M],F[M<<1],col[M<<1],ans[M];
bool mark[M];
vector<int>putin;
set<pair<int,int> >Q;
int findfa(int x){
    if(F[x]==x)return x;
    else return F[x]=findfa(F[x]);
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&A[i]);
    int nxt=0;
    for(int i=1;i<=n;i++){
        mark[A[i]]=true;
        while(mark[nxt+1])nxt++;
        fin[i]=nxt;
    }
    for(int i=1;i<=n*2;i++)
        F[i]=i;
    for(int i=1;i<=n;i++){
        while(!Q.empty()&&Q.begin()_st<=fin[i])Q.erase(Q.begin());
        if(!Q.empty()&&Q.begin()_st<A[i]){
            set<pair<int,int> >::iterator it,it1;
            int fa=findfa(Q.begin()_st),fb=findfa(Q.begin()_st+n);
            for(it=Q.begin();it!=Q.end()&&it _st<A[i];it++){
                int ffa=findfa(it _st),ffb=findfa(it _st+n);
                if(ffa==fb||ffb==fa){
                    puts("NIE");
                    return 0;
                }else{
                    F[ffa]=fa;
                    F[ffb]=fb;
                }
            }
            putin.clear();
            for(it=Q.begin();it!=Q.end()&&it _st<A[i];it++){//诡异的合并操作
                if(it==Q.begin()){
                    putin.push_back(it _st);
                    putin.push_back(it _ed);
                }else{
                    if(putin[putin.size()-1]+1==it _st)putin[putin.size()-1]=it _ed;
                    else{
                        putin.push_back(it _st);
                        putin.push_back(it _ed);
                    }
                }
            }
            for(it=Q.begin();it!=Q.end()&&it _st<A[i];){
                it1=it++;
                Q.erase(it1);
            }
            for(int j=1;j<putin.size();j+=2)
                Q.insert(mp(putin[j-1],putin[j]));
            F[A[i]]=fb;F[A[i]+n]=fa;
        }
        Q.insert(mp(A[i],A[i]));
    }
    puts("TAK");
    for(int i=1;i<=n;i++){
        int fa=findfa(i),fb=findfa(i+n);
        if(!col[fa]||col[fb]==-1&&col[fa]==1){
            col[fa]=1;
            ans[i]=1;
            col[fa+((fa>n)?-n:n)]=-1;
        }else{
            col[fb]=1;
            ans[i]=2;
            col[fb+((fb>n)?-n:n)]=-1;
        }
    }
    for(int i=1;i<=n;i++)
        printf("%d%c",ans[A[i]],(i==n)?'\n':' ');
    return 0;
}

其他大犇们都在写稳定的 O(nlogn) 算法。蒟蒻浑身发抖…

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值