链接: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 wxh010910
freopen( "data.in", "r", stdin );
#endif
build( 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;
}