[NOIP2015 提高组] 运输计划

Posted yukari1735 aka 泉 こなた

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOIP2015 提高组] 运输计划相关的知识,希望对你有一定的参考价值。

给出一颗点数为 \\(n\\) 的无根树,边有边权。

接着给出 \\(m\\) 条简单路径,一条简单路径 \\(p\\) 的花费 \\(\\sigma_p\\) 被定义为其经过的所有边的边权之和。

你可以选择任意一条边,将其边权置为 \\(0\\)

最小化这些路径花费的最大值,输出这个最小化的最大值。

设路径最大值为 \\(w\\),则 \\(w\\) 越大越容易满足条件,这个东西具有单调性。

于是考虑二分答案,若我们当前的二分值是 \\(w\\),则 \\(\\sigma_p\\leq w\\) 的路径 \\(p\\) 都可以不考虑。

而所有 \\(\\sigma_p>w\\) 的路径 \\(p\\) 都是不满足条件的,所以我们需要将它们全部减小。

但是我们仅可以修改一条边为 \\(0\\),所以修改的位置必定是所有路径的一个交边。若这些路径的并非交于至少一边,那么条件就无法满足,直接返回。

那如果我们运气较好,这些路径都交于了至少一边,那么设 \\(\\max\\\\sigma_p\\\\)\\(t\\),遍历所有交边,若有一边 \\(e\\) 满足 \\(t-val_e\\leq w\\),那么条件就可以被满足。

而判断这些路径是否有交边,我们可以将 \\(k\\) 个路径上的所有边 \\(+1\\),最后若有一边 \\(e\\) 的值等于 \\(k\\),则 \\(e\\) 是这 \\(k\\) 个路径的一条交边。

这个操作可以用树上差分来完成。注意到每次树上差分都要求 LCA,于是我们可以先将每条路径的 LCA 预处理出来,这样就省掉了一个 \\(\\log\\),单次时间复杂度为 \\(O(n)\\)

加上二分答案,总时间复杂度就是 \\(O(n\\log n)\\)

注意卡常,可以先将树的 DFS 序存下来,每次树上差分直接在 DFS 序上跑而不是 DFS。

# include <cstdio>
# include <iostream>
# include <utility>

namespace IO
	inline int read()
		int ret = 0 , ti = 1;
		char u = getchar();
		while( ! isdigit( u ) ) if( u == \'-\' ) ti = -1; u = getchar(); 
		while( isdigit( u ) ) ret = ret * 10 + u - \'0\' , u = getchar();
		return ret * ti;
	


using namespace std;

const int N = 3e5 + 225;

struct Edge
	int nxt , to , val;
edge[ N << 1 ];

# define forE( u , i ) for( i = head[ u ] ; i ; i = edge[ i ] . nxt )

int head[ N ] , eg;
int n , m , t;

pair< int , int > r[ N ];
# define _u first
# define _v second

int dis[ N ] , tp[ N ] , siz[ N ] , dep[ N ] , fa[ N ] , ch[ N ] , s[ N ] , dfsr[ N ] , df;
int lca[ N ] , d[ N ] , q[ N ] , cnt; 
int f[ N ];

void dfs1( int u , int f )
	dep[ u ] = dep[ f ] + 1 , fa[ u ] = f , siz[ u ] = 1 , dfsr[ ++ df ] = u;
	int i;
	forE( u , i )
		int v = edge[ i ] . to , w = edge[ i ] . val;
		if( v == f ) continue;
		dis[ v ] = dis[ u ] + w , s[ v ] = w;
		dfs1( v , u );
		siz[ u ] += siz[ v ];
		if( siz[ v ] > siz[ ch[ u ] ] ) ch[ u ] = v;
	


void dfs2( int u , int top )
	tp[ u ] = top;
	int i;
	if( ch[ u ] ) dfs2( ch[ u ] , top );
	forE( u , i )
		int v = edge[ i ] . to;
		if( v == fa[ u ] || v == ch[ u ] ) continue;
		dfs2( v , v );
	


int LCA( int u , int v )
	while( tp[ u ] != tp[ v ] )
		if( dep[ tp[ u ] ] < dep[ tp[ v ] ] ) swap( u , v );
		u = fa[ tp[ u ] ];
	
	return dep[ u ] > dep[ v ] ? v : u;


void add_edge( int u , int v , int w , int op )
	edge[ ++ eg ] =  head[ u ] , v , w ;
	head[ u ] = eg;
	if( op )
		edge[ ++ eg ] =  head[ v ] , u , w ;
		head[ v ] = eg;
	


bool check( int x )
	for( int i = 1 ; i <= n ; i ++ ) f[ i ] = 0;
	cnt = 0;
	for( int i = 1 ; i <= m ; i ++ )
		if( d[ i ] > x ) ++ cnt , ++ f[ r[ i ] . _u ] , ++ f[ r[ i ] . _v ] , f[ lca[ i ] ] -= 2;
	for( int i = n ; i >= 1 ; i -- ) f[ fa[ dfsr[ i ] ] ] += f[ dfsr[ i ] ];
	for( int i = 1 ; i <= n ; i ++ ) if( f[ i ] == cnt && t - s[ i ] <= x ) return 1;
	return 0;


void solve()
	for( int i = 1 ; i <= m ; i ++ )
		lca[ i ] = LCA( r[ i ] . _u , r[ i ] . _v );
		d[ i ] = dis[ r[ i ] . _u ] + dis[ r[ i ]. _v ] - ( dis[ lca[ i ] ] << 1 );
		t = max( t , d[ i ] );
	
	int l = 0 , r = t , ans = 0;
	while( l <= r )
		int mid = l + r >> 1;
		if( check( mid ) ) r = mid - 1;
		else l = mid + 1 , ans = l;
	
	printf( "%d\\n" , ans );


void input()
	n = IO :: read() , m = IO :: read();
	for( int i = 1 ; i <= n - 1 ; i ++ )
		int u = IO :: read() , v = IO :: read() , w = IO :: read();
		add_edge( u , v , w , 1 );
	
	for( int i = 1 ; i <= m ; i ++ )
		int u = IO :: read() , v = IO :: read();
		r[ i ] = make_pair( u , v );
	


int main()
	input();
	dfs1( 1 , 0 );
	dfs2( 1 , 1 );
	solve();
	return 0;

以上是关于[NOIP2015 提高组] 运输计划的主要内容,如果未能解决你的问题,请参考以下文章

[NOIP2015 提高组] 运输计划

[NOIP2015] 提高组 洛谷P2680 运输计划

[NOIP2015 提高组] 运输计划 lca 正确做法

洛谷 P2680 [NOIP2015 提高组] 运输计划(二分,树上查分,树上倍增,LCA)

如何评价NOIP2015提高组复赛试题

[NOIP2013 提高组] 货车运输