【洛谷】1983 车站分级

真是一道好题啊,是今天下午的考试题!

但是并没有做起,如果是1e3的数据范围随便搞搞就好了,但是题我竟然读错了……很尴尬

花了很多时间理清楚这道题1e5的数据范围该怎么思考

 

首先从级别的划分可以看出,对于某一车次,有一些点是高人一等的,而高人一等的点比低人一等的点就当前来说低1个级别,那么是不是可以将每一个低人一等的点向高人一等的点连边呢?是的,这样的话问题就成了要求级别最高的点最高的级别是什么,那么要求这个点的级别,是否需要先求出低它一等的、低它两等、三等……的所有级别分别是什么,这显然是一个拓扑排序的板板题

 

但是这样的边数是N^2级别的,所以对于1e5的数据瞬间爆炸……

 

不过今天下午考试的时候可以苟40分啦,数据范围1e3的4组, 但是我很垃圾所以题都读错了就别说什么苟分的问题了 

 

考虑怎么优化建边的过程,首先可以不用将需要连边的区间中每一个点都连上,这样既耗时又耗空间,正确的做法是将这一个区间的点选出一个代表来,然后让所有低人一等的点连像这个代表,然后代表向每一个高人一等的点连边,这样空间就下来了,N的级别

 

但是时间还是无法承受如此暴力的建边过程最坏 O(N^2),考虑如何优化

 

区间建边操作特别的会想到什么,线段树!!!对了,用线段树再次加速这个过程,使原先需要连的N级别的点变成了log级别的一块一块整区间“线段点”,这样将每一个线段树上的儿子向其爸爸连边,然后就可以跑拓扑了

 

但是有一个问题,是不是所有的点都需要计算它的level的贡献(+1)呢?其实不然,为了解决建边的时空复杂度我们建立了很多个虚结点,所以这些虚结点根本不需要去管,直接跳过计算level的过程或者直接继承上一个连接它的结点的level信息就好了

 

但是这样是错的,因为直接继承代表着只能继承唯一一个前继结点,但是很可能它有一堆前继结点,导致覆盖了原本更新得到的最大值,所以不能图懒,还是要挨个挨个更新的!!!(我WA了好久,一直90分,还是写这篇博客的时候顿悟了马上改然后马上就A了)

 

数组要开大一点,不然会TLE;文件的freopen要注释否则会评测会反馈MLE(鬼知道为什么写文件读入会MLE,洛谷……唉)

 

AC代码:

 

# include <bits/stdc++.h>

const  int  N = 1e6 + 5 ;

std :: map < int , int >  mp ;
std :: queue < int >  q ;

int  ch [ N << 3 ] [ 2 ] ;
int  head [ N ] , nxt [ N ] , to [ N ] , cn ;
int  pos [ N ] , in [ N ] , level [ N ] ;
int  nd , n , m , lst , now , void_point , tail , root , x ;

int  maxx ( int  a , int  b ) {
    return  a > b ? a : b ;
}

struct  Seg_Tree {
    void  create ( int  u , int  v ) {
        in [ v ] ++ ;
        cn ++ ;
        to [ cn ] = v ;
        nxt [ cn ] = head [ u ] ;
        head [ u ] = cn ;
    }
    int  build ( int  l , int  r ) {
        int  nd = ++ tail ;
        if ( l == r ) {
            pos [ l ] = nd ;
            mp [ nd ] = l ; 
            return  nd ;
        }
        int  mid = l + r >> 1 ;
        ch [ nd ] [ 0 ] = build ( l , mid ) ;
        create ( ch [ nd ] [ 0 ] , nd ) ;
        ch [ nd ] [ 1 ] = build ( mid + 1 , r ) ;
        create ( ch [ nd ] [ 1 ] , nd ) ;
        return  nd ;
    }
    void  modify ( int  nd , int  l , int  r , int  L , int  R , int  void_node ) {
        if ( L <= l  &&  r <= R ) {
            create ( nd , void_node ) ;
            return  ;
        }
        int  mid = l + r >> 1 ;
        if ( L <= mid )  modify ( ch [ nd ] [ 0 ] , l , mid , L , R , void_node ) ;
        if ( R > mid )  modify ( ch [ nd ] [ 1 ] , mid + 1 , r , L , R , void_node ) ;
    }
    void  top_sort ( ) {
        for ( int  i = 1 ; i <= tail ; i ++ ) 
            if ( ! in [ i ] ) {
                q . push ( i ) ;
                if ( mp [ i ] )
                    level [ i ] ++ ;
            }
        while ( ! q . empty ( ) ) {
            int  tmp = q . front ( ) , v ;
            q . pop ( ) ;
            for ( int  i = head [ tmp ] ; i ; i = nxt [ i ] ) {
                v = to [ i ] ;
                in [ v ] -- ;
                if ( mp [ v ] )  level [ v ] = std :: max ( level [ v ] , level [ tmp ] + 1 ) ;
                else  level [ v ] = std :: max ( level [ v ] , level [ tmp ] ) ; // 必须要比较不然会WA第9个点,我原本打算复制90分代码的,但是写着写着就想到了问题和解决方案,所以,必须要给自己一点消化和思考的时间啊
                if ( ! in [ v ] ) {
                    q . push ( v ) ;
                }
            }
        }
    }
} 
Gyx ;

int  main ( ) {
    scanf ( "%d%d" , & n , & m ) ;
    root = Gyx . build ( 1 , n ) ;
    for ( int  i = 1 ; i <= m ; i ++ ) {
        scanf ( "%d%d" , & x , & lst ) ;
        void_point = ++ tail ;
        Gyx . create ( void_point , pos [ lst ] ) ;
        for ( int  j = 1 ; j < x ; j ++ ) {
            scanf ( "%d" , & now ) ;
            Gyx . create ( void_point , pos [ now ] ) ;
            Gyx . modify ( root , 1 , n , lst + 1 , now - 1 , void_point ) ;
            lst = now ;
        }
    }
    Gyx . top_sort ( ) ;
    int  ans = 0 ;
    for ( int  i = 1 ; i <= n ; i ++ )
        ans = maxx ( ans , level [ pos [ i ] ] ) ;
    printf ( "%d" , ans ) ;
    return  0 ;
}

 

睡了睡了,困死了……

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值