真是一道好题啊,是今天下午的考试题!
但是并没有做起,如果是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 ;
}
睡了睡了,困死了……