zoukankan      html  css  js  c++  java
  • [HDU6793] Tokitsukaze and Colorful Tree

    题目

    又是一个条历新年,窗前的灼之花又盛开了。

    时隔多年,现在只有这一棵树上盛开着残存的 (n) 朵灼之花了。

    尽管如此,这些灼之 花仍散发出不同色彩的微弱的光芒。 灼之花的生命极为短暂,但它的花色与光亮瞬息万变。

    作为条条的粉丝,Little Q 细致 地记录了最初每朵灼之花的花色 (c_i) 和光亮 (l_i) ,以及接下来的 (m) 秒中灼之花的变化,第 (i) 秒会发生下面两种变化之一:

    • " (1 t_i c_i') " 第 (t_i) 朵灼之花的颜色变为 (c_i')
    • " (2 t_i l_i') " 第 (t_i) 朵灼之花的光亮变为 (l_i')

    灼之花一齐盛开的景象令人过目难忘,其美丽值为每对生长在不同枝杈的相同颜色的灼之花的光亮值的异或和:

    [sum_{u,v,c_u=c_v,\ operatorname{LCA}(u,v) ot=u,\operatorname{LCA}(u,v) ot=v}l_uoplus l_v ]

    数据范围: (1le c_i,c_i'le n,mle 10^6, 0le l_i,l_i'le 2^{20})

    原题:[HDU6793]Tokitsukaze and Colorful Tree

    分析

    首先需要一些基础的分析。

    第一步,异或首先按位进行拆分。

    第二步,显然,我们总可以将变化拆分成向某个颜色的集合中插入或者删除一个点

    第三步,接着我们需要考虑动态地维护插入一个点或删除一个点之后,同种颜色中异或为 1 的符合条件的点对数量

    我们只需要考虑与变化的点有关的点对。异或为 1 可以简单地分类讨论,我们需要考虑 (operatorname{LCA}) 的限制。

    不难发现,我们只需要减去下列不合法的点即可:

    1. 对应点在根到当前点的路径上

    2. 对应点在当前点的子树内

    第一个部分可以考虑维护贡献,等价于每次修改子树,也就是 DFS 序上的一段区间,区间加 - 单点查。

    第二个部分可以考虑统计贡献,直接在 DFS 序上面维护,单点加 - 区间查。

    如果需要在线算法,就可以用维护 (n)(splay) ,分别维护 (n) 种颜色的点的部分 DFS 序。

    如果需要离线算法,就可以直接使用整棵树的 DFS 序,然后用 4 个 BIT 分别维护。

    时间复杂度 (O(mlog_2nlog_2l))

    本题的一些有价值的点:

    1. 对于变化操作的拆分,转化为简单的情形。
    2. 统计贡献维护贡献两种思想。

    代码

    由于这是模拟赛里面的题目,所以没有处理多组数据。

    #include <cstdio>
    #include <vector>
    using namespace std;
    
    typedef long long LL;
    
    #define int LL 
    
    const int MAXN = 1e5 + 5, MAXS = MAXN << 1;
    
    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' );
    }
    
    struct edge
    {
    	int to, nxt;
    }Graph[MAXN << 1];
    
    struct oper
    {
    	int typ, pos, nVal, sta;
    	oper() { typ = pos = nVal = sta = 0; }
    	oper( int a, int b, int c, int d ) { typ = a, pos = b, nVal = c, sta = d; } 
    };
    
    //1->add, 0->delete
    
    vector<oper> O[MAXN];
    
    LL ans[MAXN]; 
    int dif[MAXN];
    
    int C[MAXN], L[MAXN];
    int head[MAXN], DFN[MAXN], siz[MAXN];
    int N, M, cnt, ID, D;
    int tot[2]; LL all;
    
    struct BinIndTree
    {
    	int BIT[MAXN];
    	
    	void up( int &x ) { x += ( x & ( -x ) ); }
    	void down( int &x ) { x -= ( x & ( -x ) ); }
    	void update( int x, const int v ) { for( ; x <= N ; up( x ) ) BIT[x] += v; }
    	int getSum( int x ) { int ret = 0; for( ; x ; down( x ) ) ret += BIT[x]; return ret; }
    	int query( const int l, const int r ) { return getSum( r ) - getSum( l - 1 ); }
    };
    
    struct RtoU : BinIndTree
    {
    	int Q( const int x ) { return getSum( DFN[x] ); }
    	void upt( const int x, const int v ) { update( DFN[x], v ), update( DFN[x] + siz[x], -v ); };
    }R[2];
    
    struct SubT : BinIndTree
    {
    	int Q( const int x ) { return query( DFN[x], DFN[x] + siz[x] - 1 ); }
    	void upt( const int x, const int v ) { update( DFN[x], v ); }
    }S[2];
    
    void addEdge( const int from, const int to )
    {
    	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    	head[from] = cnt;
    }
    
    void init( const int u, const int fa )
    {
    	DFN[u] = ++ ID, siz[u] = 1;
    	for( int i = head[u], v ; i ; i = Graph[i].nxt )
    		if( ( v = Graph[i].to ) ^ fa )
    			init( v, u ), siz[u] += siz[v];
    }
    
    int get( const int u, const int t )
    {
    	int a = R[t].Q( u ), b = S[t].Q( u );
    	return a + b;
    }
    
    void launch( oper cur )
    {
    	int u = cur.pos, delt = cur.nVal >> D & 1, val;
    	if( cur.typ )
    	{
    		val = tot[delt ^ 1] - get( u, delt ^ 1 );
    		R[delt].upt( u, 1 ), S[delt].upt( u, 1 ), tot[delt] ++;
    	}
    	else
    	{
    		R[delt].upt( u, -1 ), S[delt].upt( u, -1 ), tot[delt] --;
    		val = - tot[delt ^ 1] + get( u, delt ^ 1 );
    	}
    	all += val; 
    }
    
    signed main()
    {
    	read( N );
    	for( int i = 1 ; i <= N ; i ++ ) read( C[i] );
    	for( int i = 1 ; i <= N ; i ++ ) read( L[i] );
    	for( int i = 1, a, b ; i < N ; i ++ ) read( a ), read( b ), addEdge( a, b ), addEdge( b, a );
    	init( 1, 0 ), read( M );
    	for( int i = 1 ; i <= N ; i ++ ) O[C[i]].push_back( oper( 1, i, L[i], 0 ) );
    	for( int i = 1 ; i <= M ; i ++ )
    	{
    		int opt, x, y;
    		read( opt ), read( x ), read( y );
    		if( opt == 1 )
    			O[C[x]].push_back( oper( 0, x, L[x], i ) ),
    			O[C[x] = y].push_back( oper( 1, x, L[x], i ) );
    		else
    			O[C[x]].push_back( oper( 0, x, L[x], i ) ),
    			O[C[x]].push_back( oper( 1, x, L[x] = y, i ) );
    	}
    	for( int i = 1 ; i <= N ; i ++ ) O[C[i]].push_back( oper( 0, i, L[i], M + 1 ) );
    	for( D = 0 ; D < 20 ; D ++ )
    	{
    		for( int i = 0 ; i <= M ; i ++ ) dif[i] = 0;
    		for( int col = 1 ; col <= N ; col ++ )
    		{
    			tot[0] = tot[1] = 0;
    			int lim = ( int ) O[col].size(), ed;
    			for( int k = 0, r = 0 ; k < lim ; )
    			{
    				for( r = k ; r < lim && O[col][r].sta == O[col][k].sta ; r ++ );
    				ed = r == lim ? M + 1 : O[col][r].sta;
    				for( ; k < r ; k ++ ) launch( O[col][k] );
    				dif[O[col][k - 1].sta] += all, dif[ed] -= all;
    			}
    		}
    		for( int i = 1 ; i <= M ; i ++ ) dif[i] += dif[i - 1];
    		for( int i = 0 ; i <= M ; i ++ ) ans[i] += ( 1ll << D ) * dif[i];
    	}
    	for( int i = 0 ; i <= M ; i ++ ) write( ans[i] ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    状态模式
    简单密码再次加密
    服务层定义自己的服务异常类
    必备网络基础知识(持续补充)
    MongoDB基础入门
    Git命令整理
    算法基础(四)
    RabbitMQ消息队列
    设计模式(23种)
    二叉树知识点
  • 原文地址:https://www.cnblogs.com/crashed/p/13550933.html
Copyright © 2011-2022 走看看