zoukankan      html  css  js  c++  java
  • [2010国家集训队]Crash的旅游计划

    题目

      点这里看题目。
       BZOJ 上面是权限题目。

    分析

      第 K 小的问题无非两种方法——构造法或者二分法。
      很显然如果构造的话在这里不太好处理,因此我们可以对于每一个点(u),二分一下这个第 K 小路的长度。
      检查的方法很简单,也就是计算一下从(u)出发的路径中,是否有 K 条以上的路径的长度小于等于二分长度。因此,我们只需要想办法快速求出(u)出发的路径中有多少条的长度小于等于二分长度
      考虑一次点分治进行检查。以(u)为分治中心的时候,可以计算连通块内部的路径;剩下的路径不在连通块内部,但是一定以(u)的一个分治祖先作为路径的 LCA 。因此我们可以枚举分治祖先然后计算除(u)所在子树后剩下的路径。
      由于本题的二分需要多次检查,我们就需要建立起点分树。对于点分树上的每一个点,我们惯例维护两个信息,一个是分治连通块中所有点到该点的距离,另一个是分治连通块中所有点到点分树父亲的距离。第一个用于直接计算,第二个用于减去多余的贡献。预处理的时候,将上述两个信息存下来(比如 vector )后排序。检查的时候可以直接二分了。
      时间:外部枚举和二分为(O(nlog_2V)),跳点分树(O(log_2n)),内部二分(O(log_2n)),其中(V=sum_{(u,v)in E} w(u,v)),即边权和。总体时间复杂度(O(nlog_2^2nlog_2V))

    代码

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 1e5 + 5, MAXLOG = 20;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0;char s = getchar();int f = 1;
    	while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
    	while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
    	x *= f;
    }
    
    template<typename _T>
    void write( _T x )
    {
    	if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
    	if( 9 < x ){ write( x / 10 ); }
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    struct edge
    {
    	int to, nxt, w;
    }Graph[MAXN << 1];
    
    vector<int> dist[2][MAXN];
    
    int f[MAXN][MAXLOG], dis[MAXN][MAXLOG], dep[MAXN];
    int head[MAXN], siz[MAXN], mx[MAXN];
    int N, K, all, cnt;
    bool vis[MAXN];
    
    void upt( int &x, const int v ) { x = MAX( x, v ); }
    bool visible( const int u, const int fa ) { return u ^ fa && ! vis[u]; }
    
    void addEdge( const int from, const int to, const int W )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from], Graph[cnt].w = W;
    	head[from] = cnt;
    }
    
    int getCen( const int u, const int fa )
    {
    	int tmp, ret = 0;
    	mx[u] = 0, siz[u] = 1;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( visible( v = Graph[i].to, fa ) )
    		{
    			tmp = getCen( v, u );
    			siz[u] += siz[v], upt( mx[u], siz[v] );
    			if( mx[tmp] < mx[ret] ) ret = tmp;
    		}
    	upt( mx[u], all - siz[u] );
    	if( mx[u] < mx[ret] ) ret = u;
    	return ret; 
    }
    
    void DFS( const int u, const int fa, const int rt, const int d )
    {
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( visible( v = Graph[i].to, fa ) )
    			f[v][++ dep[v]] = rt, dis[v][dep[v]] = d + Graph[i].w,
    			dist[0][rt].push_back( dis[v][dep[v]] ), dist[1][rt].push_back( dis[v][dep[v] - 1] ),
    			DFS( v, u, rt, d + Graph[i].w );
    }
    
    void divide( const int u )
    {
    	vis[u] = true; DFS( u, 0, u, 0 ); int tmp = all;
    	dist[1][u].push_back( dis[u][dep[u]] ), dist[0][u].push_back( 0 );
    	sort( dist[1][u].begin(), dist[1][u].end() ), sort( dist[0][u].begin(), dist[0][u].end() );
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ! vis[v = Graph[i].to] )
    			all = siz[v] > siz[u] ? tmp - siz[u] : siz[v],
    			divide( getCen( v, u ) );
    }
    
    int cal( const int u, const bool mod, const int len )
    {
    	int l = 0, r = dist[mod][u].size() - 1, mid;
    	if( len < dist[mod][u][l] ) return 0;
    	if( dist[mod][u][r] <= len ) return r + 1;
    	while( r - l > 1 )
    	{
    		if( dist[mod][u][mid = l + r >> 1] <= len ) l = mid;
    		else r = mid - 1;
    	}
    	if( dist[mod][u][r] <= len ) return r + 1;
    	return l + 1;
    }
    
    int chk( const int u, const int len )
    {
    	int ret = cal( u, 0, len ) - 1; //注意不能有距离为 0 的旅游路径!
    	for( int i = dep[u] ; i ; i -- )
    	{
    		ret += cal( f[u][i], 0, len - dis[u][i] );
    		ret -= cal( f[u][i + 1], 1, len - dis[u][i] );
    	}
    	return ret;
    }
    
    int main()
    {
    	int l, r, tot = 0, mid;
    	read( N ), read( K );
    	for( int i = 1, a, b, c ; i < N ; i ++ )
    		read( a ), read( b ), read( c ), addEdge( a, b, c ), addEdge( b, a, c ), tot += c;
    	mx[0] = all = N, divide( getCen( 1, 0 ) );
    	for( int i = 1 ; i <= N ; i ++ ) f[i][dep[i] + 1] = i;
    	for( int x = 1 ; x <= N ; x ++ )
    	{
    		l = 0, r = tot;
    		while( r - l > 1 )
    		{
    			if( chk( x, mid = l + r >> 1 ) >= K ) r = mid;
    			else l = mid + 1;
    		}
    		if( chk( x, l ) >= K ) write( l ), putchar( '
    ' );
    		else write( r ), putchar( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    Chapter 23: Termination Handlers(2)Understanding Termination Handlers by Example(6)
    【原】常用HTML
    [导入]化州特产~
    [导入]又大一岁,自己祝自己生日快乐~
    [导入]新视觉LOGO~
    [导入]看清楚中国移动的新旧资费
    [导入]AJAX .NET版本之间的区别
    泛型简介
    关于MongoDB的group分组
    【javascript继承】之——原型链继承和类式继承
  • 原文地址:https://www.cnblogs.com/crashed/p/12583938.html
Copyright © 2011-2022 走看看