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

链接: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值