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;
    }
    
  • 相关阅读:
    小程序源码丢失了怎么在微信平台反编译找回
    做前端技术方案选型的时候,你是怎么做决策的?
    小程序源码丢失了怎么在微信平台反编译找回
    关于form.submit()不能提交表单的错误原因
    IE 8兼容:X-UA-Compatible的解释
    常用的CSS Hack技术集锦
    PHP制作验证码
    利用原生JavaScript获取样式的方式小结
    利用Javascript获取当前日期的农历日期
    教你利用iframe在网页中显示天气
  • 原文地址:https://www.cnblogs.com/crashed/p/14659936.html
Copyright © 2011-2022 走看看