您的位置:首页 > 理论基础 > 计算机网络

LOJ6005 「网络流 24 题 - 6」 最长递增子序列 坠大流

2017-12-07 10:55 281 查看

大家都很强, 可与之共勉 。

题意:

  给定正整数序列 x1~xn ,以下递增子序列均为非严格递增。

计算其最长递增子序列的长度s。

计算从给定的序列中最多可取出多少个长度为s的递增子序列。

如果允许在取出的序列中多次使用x1和xn​ ,则从给定序列中最多可取出多少个长度为s的递增子序列。

题解:

  我忘了O(nlogn)的最长不递减子序列怎么写

  第一问即是裸题,直接单调栈优化的最长上升子序列求解就可以了。

  第二问用网络流,考虑如何建边才能使得每一条路径都是长度为s的子序列且每个数只能使用一次。

我们已经预处理出了f[i]数组表示以第i个结尾的最长不递减子序列的长度。设第一问的答案为k。

然后拆点,每个点a,拆为(a1,a2),然后a1向a2连一条容量为1的弧,保证每个点一定只使用一次。

建立源点S,汇点T。

其中对于f[u]=1的点,S向u1连一条容量为1的弧,对于f[v]=k的点,v2向T连一条容量为1的弧。

然后对于i>j且a[j]≤a[i],我们连一条j2→i1,流量为1的边。

  然后跑最大流就好了,一定保证从S到T的路径长度为k,最大流就是方案数。

  第三问在第二问的基础上完成,我们发现要求的只是a1,an可以任意多次使用,那么把与之相关的四条边容量改为+∞。即是S→11,11→12,n1→n2,n2→T。

  就保证了正确性。注意一些细节

# include <bits/stdc++.h>

# define N 5005

class Network  {
private :
struct edge  {
int to, w, nxt ;
edge ( ) {        }
edge ( int to, int w, int nxt ) : to ( to ), w ( w ), nxt ( nxt ) {        }
} g [60010 << 1] ;

int head
, cur
, ecnt ;
int S, T , dep
;

inline int dfs ( int u, int a )  {
if ( u == T || ! a )  return a ;
int flow = 0, v, f ;
for ( int& i = cur [u] ; i ; i = g [i].nxt )  {
v = g [i].to ;
if ( dep [v] == dep [u] + 1 )  {
f = dfs ( v, std :: min ( g [i].w, a - flow ) ) ;
g [i].w -= f, g [i ^ 1].w += f ;
flow += f ;
if ( a == flow )  return a ;
}
}
if ( ! flow )  dep [u] = -1 ;
return flow ;
}

inline bool bfs ( int S, int T )  {
static std :: queue < int > q ;
memset ( dep, 0, sizeof ( int ) * ( T + 1 ) ) ;
dep [S] = 1 ;
q.push ( S ) ;
while ( ! q.empty ( ) )  {
int u = q.front ( ) ;  q.pop ( ) ;
for ( int i = head [u] ; i ; i = g [i].nxt )  {
int& v = g [i].to ;
if ( g [i].w &&  ! dep [v] )  {
dep [v] = dep [u] + 1 ;
q.push ( v ) ;
}
}
}
return dep [T] ;
}
public :
Network ( )  {    ecnt = 1 ; }

inline void add_edge ( int u, int v, int w )  {
g [++ ecnt] = edge ( v, w, head [u] ) ;     head [u] = ecnt ;
g [++ ecnt] = edge ( u, 0, head [v] ) ;     head [v] = ecnt ;
}

inline void clear ( )  {
ecnt = 1 ;
memset ( head, 0, sizeof head ) ;

}
inline int dinic ( int S, int T )  {
this -> S = S, this -> T = T ;
int rt = 0 ;
while ( bfs ( S, T ) )    {
memcpy ( cur, head, sizeof ( int ) * ( T + 1 ) ) ;
rt += dfs ( S, 0x3f3f3f3f ) ;
}
return rt ;
}
} Lazer ;

int f
;

inline int Lis ( int* s, int n )  {
if ( n == 0 )   return 0 ;
int* src = new int [( const int ) n + 1] ;
int len ( 1 ) ;
src [1] = s [1] ;
f [1] = 1 ;
for ( int i = 2 ; i <= n ; ++ i )  {
src [f [i] = ( s [i] >= src [len] ) ? ( ++ len ) : ( std :: upper_bound ( src + 1, src + 1 + len, s [i] ) - src )] = s [i] ;
}
return len ;
}

int a
;

int main ( )  {
int n ;
scanf ( "%d", & n ) ;

for ( int i = 1 ; i <= n ; ++ i )  {
scanf ( "%d", a + i ) ;
}

int ans = Lis ( a, n ) ;

printf ( "%d\n", ans ) ;

const int S = n * 2 + 1, T = n * 2 + 2 ;

for ( int i = 1 ; i <= n ; ++ i )  {
if ( f [i] == ans )  {
Lazer.add_edge ( i + n, T, 1 ) ;
}  // no else
if ( f [i] == 1 )  {
Lazer.add_edge ( S, i, 1 ) ;
}
Lazer.add_edge ( i, i + n, 1 ) ;
}

for ( int i = 1 ; i <= n ; ++ i )
for ( int j = 1 ; j < i ; ++ j )
if ( f [i] == f [j] + 1 && a [i] >= a [j] )  {
Lazer.add_edge ( j + n, i, 1 ) ;
}
printf ( "%d\n", Lazer.dinic ( S, T ) ) ;

Lazer.clear ( ) ;

for ( int i = 1 ; i <= n ; ++ i )  {
if ( f [i] == ans )  {
Lazer.add_edge ( i + n, T, i == n ? 0x3f3f3f3f : 1 ) ;
}
if ( f [i] == 1 )  {  // no else
Lazer.add_edge ( S, i, i == 1 ? 0x3f3f3f3f : 1 ) ;
}
Lazer.add_edge ( i, i + n, i == 1 || i == n ? 0x3f3f3f3f : 1 ) ;
}

for ( int i = 1 ; i <= n ; ++ i )
for ( int j = 1 ; j < i ; ++ j )
if ( f [i] == f [j] + 1 && a [i] >= a [j] )  {
Lazer.add_edge ( j + n, i, 1 ) ;
}
printf ( "%d\n", Lazer.dinic ( S, T ) ) ;
return 0 ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: