zoukankan      html  css  js  c++  java
  • [ZJOI2015]幻想乡战略游戏

    题目

    点这里看题目。

    分析

    如果你熟悉问题的结构的话,你会发现题目要求的相当于是树上带权重心

    关于这一点的说明:

    考虑现在将任意一点 (u) 提作根,并设 (sd_u)(u) 子树内的 (d) 之和。

    那么考虑 (u) 的一个儿子 (v) ,如果现在将根从 (u) 颠倒到 (v) ,那么答案会发生什么变化?

    不妨设 ((u,v)) 的权为 (l) 。如果换到了 (v) 会更优,则有:

    [egin{aligned} sd_v imes l-(sd_u-sd_v) imes l&<0\ 2sd_v&>sd_u end{aligned} ]

    当不存在一个儿子 (v) 满足这个条件的时候,可以发现 (u) 就是我们要求的答案。而不难发现最终的 (u) 一定满足树上带权重心的性质。

    那么我们只需要干两件事情:

    1. 可以快速找出树上带权重心是哪一个。
    2. 可以快速查询 (u) 确定时候的 (sum_v d_v imes dist(u,v))

    后一个很好做,我们可以直接点分树维护一下,不是问题。

    而前一个问题则比较棘手,直接模拟会退化到 (O(n^2)) ,会 T 的......

    为了优化这个过程,我们不难想到最开始选择根,可以直接选树的大小重心;找到了目标子树之后,我们也可以将目标子树的大小重心提作新的根。这样每迭代一次,树的大小必然折半,可以保证迭代次数。

    考虑同样用点分树维护这个过程,我们实际上只需要维护 " 换根 " 的过程。设当前点为 (u) ,目标为 (v) 的子树,而 (v) 的子树的重心是 (c) ,那么将 (c) 提作新根的时候,就相当于将 (v)(c) 范围下的子树加上了 (u)(v) 以外的内容。注意到之后我们一定不会回到 (u) ,因此可以直接在点分树上,将 (c) 及其祖先分治块的大小全部加上外部的权。

    换根.png

    u 会影响 c 作根时 v 所在的子树

    这样做的时间即是 (O(nlog_2^2n)) 的。

    在本题中会有一个 20 的常数。

    代码

    #include <cstdio>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef long long LL;
    
    const int MAXN = 1e5 + 5, MAXLOG = 17;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && 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;
    	if( 9 < x ) write( x / 10 );
    	putchar( x % 10 + '0' );
    }
    
    template<typename _T>
    _T MIN( const _T a, const _T b )
    {
    	return a < b ? a : b;
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    struct MyGraph
    {
    	struct Edge
    	{
    		int to, nxt, w;
    	}Graph[MAXN << 1];
    	
    	int head[MAXN], cnt;
    	
    	Edge& operator [] ( const int idx ) { return Graph[idx]; }
    	void AddE( const int from, const int to, const int W = 0 ) 
    	{ AddEdge( from, to, W ), AddEdge( to, from, W ); }
    	
    	void AddEdge( const int from, const int to, const int W = 0 )
    	{
    		Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    		Graph[cnt].w = W, head[from] = cnt;
    	}
    }T, nwT;
    
    int wei[MAXN][MAXLOG], fath[MAXN][MAXLOG];
    
    LL dSu[MAXN], wSu[MAXN], faWSu[MAXN];
    int DFN[MAXN], dep[MAXN], siz[MAXN], inter[MAXN];
    int N, Q, rt;
    bool vis[MAXN];
    
    int GetCen( const int u, const int fa, const int all )
    {
    	int mx = 0, ret = 0; siz[u] = 1;
    	for( int i = T.head[u], v ; i ; i = T[i].nxt )
    		if( ! vis[v = T[i].to] && v ^ fa )
    		{
    			ret |= GetCen( v, u, all );
    			siz[u] += siz[v], mx = MAX( mx, siz[v] );
    		}
    	if( ( MAX( mx, all - siz[u] ) << 1 ) <= all ) ret = u;
    	return ret;
    }
    
    void DFS( const int u, const int fa, const int fr, const int dis, const int d )
    {
    	wei[u][d] = dis, fath[u][d] = fr;
    	for( int i = T.head[u], v ; i ; i = T[i].nxt )
    		if( ( v = T[i].to ) ^ fa && ! vis[v] )
    			DFS( v, u, fr, dis + T[i].w, d );
    }
    
    void Divide( const int u, const int all, const int d )
    {
    	dep[u] = d, DFS( u, 0, u, 0, d ), vis[u] = true;
    	for( int i = T.head[u], v, t, nxt ; i ; i = T[i].nxt )
    		if( ! vis[v = T[i].to] )
    		{
    			t = siz[v] > siz[u] ? all - siz[u] : siz[v];
    			nxt = GetCen( v, u, t ), inter[nxt] = v;
    			nwT.AddEdge( u, nxt ), Divide( nxt, t, d + 1 );
    		}
    }
    
    void Update( const int u, const int delt )
    {
    	dSu[u] += delt;
    	for( int i = dep[u] - 1, v, w ; ~ i ; i -- )
    	{
    		v = fath[u][i], w = fath[u][i + 1];
    		dSu[v] += delt, wSu[v] += 1ll * wei[u][i] * delt;
    		faWSu[w] += 1ll * wei[u][i] * delt;
    	}
    }
    
    LL GetSum( const int u )
    {
    	LL ret = wSu[u];
    	per( i, dep[u] - 1, 0 )
    		ret += wSu[fath[u][i]] - faWSu[fath[u][i + 1]]
    			 + wei[u][i] * ( dSu[fath[u][i]] - dSu[fath[u][i + 1]] );
    	return ret;
    }
    
    void Change( const int u, const LL delt ) { rep( k, 0, dep[u] ) dSu[fath[u][k]] += delt; }
    
    int GetBest( const int u )
    {
    	int ret = u; LL delt;
    	for( int i = nwT.head[u], v ; i ; i = nwT[i].nxt )
    		if( 2 * dSu[v = nwT[i].to] > dSu[u] )
    		{
    			delt = dSu[u] - dSu[v];
    			Change( inter[v], delt );
    			ret = GetBest( v );
    			Change( inter[v], - delt );
    		}
    	return ret;
    }
    
    int main()
    {
    	read( N ), read( Q );
    	rep( i, 1, N - 1 ) { int a, b, c;
    		read( a ), read( b ), read( c );
    		T.AddE( a, b, c );
    	}
    	Divide( rt = GetCen( 1, 0, N ), N, 0 );
    	for( int x, e ; Q -- ; )
    	{
    		read( x ), read( e );
    		Update( x, e );
    		x = GetBest( rt );
    		write( GetSum( x ) ), putchar( '
    ' );
    	}
    	return 0;
    }
    
  • 相关阅读:
    Dot Net WinForm 控件开发 (七) 为属性提下拉式属性编辑器
    WinForm 程序的界面多语言切换
    c#遍历HashTable
    Dot Net WinForm 控件开发 (三) 自定义类型的属性需要自定义类型转换器
    Dot Net WinForm 控件开发 (六) 为属性提供弹出式编辑对话框
    Dot Net WinForm 控件开发 (一) 写一个最简单的控件
    Dot Net WinForm 控件开发 (四) 设置属性的默认值
    Dot Net WinForm 控件开发 (二) 给控件来点描述信息
    Dot Net WinForm 控件开发 (八) 调试控件的设计时行为
    Dot Net WinForm 控件开发 (五) 复杂属性的子属性
  • 原文地址:https://www.cnblogs.com/crashed/p/14659936.html
Copyright © 2011-2022 走看看