[HDU6793] Tokitsukaze and Colorful Tree

crashed發表於2020-08-23

題目

又是一個條歷新年,窗前的灼之花又盛開了。

時隔多年,現在只有這一棵樹上盛開著殘存的 \(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)\not=u,\\\operatorname{LCA}(u,v)\not=v}l_u\oplus l_v \]

資料範圍: \(1\le c_i,c_i'\le n,m\le 10^6, 0\le 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(m\log_2n\log_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( '\n' );
	return 0;
}