UOJ#217. 【UNR #1】奇怪的线段树

来源:互联网 发布:java 编程思想笔试题 编辑:程序博客网 时间:2024/05/20 05:25

链接:http://uoj.ac/problem/217

网络流

我们先来推一波性质:

1、无解当且仅当一个节点为0且它的儿子为1,有解的话,只有深度极大的节点是有用的(访问它祖先一定也访问了)

2、任何一个区间定位都是一段连续的右儿子+一段连续的左儿子

3、性质2的逆定理成立

实际上,每个右儿子区间左端点一定不同,左儿子区间右端点不同

这意味着每个右儿子后继唯一,每个左儿子前驱唯一

我们建立网络流模型:右儿子向后继连INF边,左儿子向后继连 INF边

那么右儿子转移到左儿子怎么办呢,如果l[i(左)] = r[j(右)] + 1,那么j -> i连INF边

剩下的就是点经过次数有上下界了,跑个最小流就好了

还有一个很显然的优化就是建虚点,优化边数

#include <bits/stdc++.h>#define xx first#define yy second#define mp make_pair#define pb push_back#define fill( x, y ) memset( x, y, sizeof x )#define copy( x, y ) memcpy( x, y, sizeof x )using namespace std;typedef long long LL;typedef pair < int, int > pa;inline int read(){int sc = 0, f = 1; char ch = getchar();while( ch < '0' || ch > '9' ) { if( ch == '-' ) f = -1; ch = getchar(); }while( ch >= '0' && ch <= '9' ) sc = sc * 10 + ch - '0', ch = getchar();return sc * f;}const int MAXN = 20005;const int MAXM = 600005;namespace Flow{const int INF = 0x3f3f3f3f;struct edge { int to, nxt, flow; } e[MAXM];int head[MAXN], cur[MAXN], S, T, e_cnt = 1, q[MAXN], ql, qr, dis[MAXN];inline void Add(int x, int y, int w) { e[ ++e_cnt ] = { y, head[ x ], w }; head[ x ] = e_cnt; }inline void Addedge(int x, int y, int w) { Add( x, y, w ); Add( y, x, 0 ); }inline bool Bfs(){for( int i = 0 ; i <= T ; i++ ) dis[ i ] = 0;dis[ q[ ql = 0 ] = S ] = qr = 1;while( ql < qr ){int x = q[ ql++ ];for( int i = head[ x ] ; i ; i = e[ i ].nxt )if( e[ i ].flow && !dis[ e[ i ].to ] ) dis[ q[ qr++ ] = e[ i ].to ] = dis[ x ] + 1;}return dis[ T ];}inline int Dfs(int x, int f){if( x == T ) return f;int ret = 0;for( int &i = cur[ x ] ; i ; i = e[ i ].nxt )if( e[ i ].flow && dis[ e[ i ].to ] == dis[ x ] + 1 ){int d = Dfs( e[ i ].to, min( f - ret, e[ i ].flow ) );ret += d; e[ i ].flow -= d; e[ i ^ 1 ].flow += d;if( ret == f ) return ret;}if( !ret ) dis[ x ] = -1;return ret;}inline int Dinic(){int ret = 0;while( Bfs() ) memcpy( cur, head, sizeof head ), ret += Dfs( S, INF );return ret;}}using Flow::S;using Flow::T;using Flow::Addedge;using Flow::Dinic;using Flow::INF;int ch[MAXN][2], l[MAXN], r[MAXN], tot, SS, TT, d[MAXN], id[MAXN], new_id[MAXN], n, rt;bool type[MAXN], leaf[MAXN];vector < int > ls, rs;inline void build(int &x, int L, int R){l[ x = ++tot ] = L; r[ x ] = R;type[ x ] = read();if( L == R ) { leaf[ x ] = type[ x ]; return ; }int mid = read();build( ch[ x ][ 0 ], L, mid ); build( ch[ x ][ 1 ], mid + 1, R );if( ( type[ ch[ x ][ 0 ] ] || type[ ch[ x ][ 1 ] ] ) && !type[ x ] ) { puts( "OwO" ); exit( 0 ); }leaf[ x ] = !type[ ch[ x ][ 0 ] ] && !type[ ch[ x ][ 1 ] ] && type[ x ];ls.pb( ch[ x ][ 0 ] ); rs.pb( ch[ x ][ 1 ] );}int main(){#ifdef wxh010910freopen( "data.in", "r", stdin );#endifbuild( rt, 1, n = read() );SS = 1; TT = 5 * n; T = TT + 1;for( int i = 1 ; i < ( n << 1 ) ; i++ ) Addedge( SS, i << 1, INF ), Addedge( i << 1 | 1, TT, INF );for( int i = 1 ; i < ( n << 1 ) ; i++ ){if( type[ i ] ) Addedge( i << 1, i << 1 | 1, INF );if( leaf[ i ] ) Addedge( S, i << 1 | 1, 1 ), Addedge( i << 1, T, 1 );}for( int i = 1 ; i <= n ; i++ ) new_id[ i ] = ( n << 2 ) + i - 1;for( auto x : rs ) id[ l[ x ] ] = x;for( auto x : rs ) if( r[ x ] ^ n ) Addedge( x << 1 | 1, id[ r[ x ] + 1 ] << 1, INF ), Addedge( x << 1 | 1, new_id[ r[ x ] + 1 ], INF );for( auto x : ls ) id[ r[ x ] ] = x;for( auto x : ls ) if( l[ x ] ) Addedge( id[ l[ x ] - 1 ] << 1 | 1, x << 1, INF ), Addedge( new_id[ l[ x ] ], x << 1, INF );Dinic();Addedge( TT, SS, INF );Dinic();return printf( "%d\n", Flow::e[ Flow::e_cnt ].flow ), 0;}