Live2D

Solution -「LOCAL」Burning Flowers

  灼之花好评,条条生日快乐(假装现在 8.15)

\(\mathcal{Description}\)

  给定一棵以 \(1\) 为根的树,第 \(i\) 个结点有颜色 \(c_i\) 和光亮值 \(l_i\),定义树的权值为:

\[\sum_{\displaystyle u<v\land c_u=c_v\land\\\operatorname{LCA}(u,v)\not=u\land\operatorname{LCA}(u,v)\not=v}l_u\oplus l_v \]

  现有 \(m\) 次修改,每次修改某点的颜色或光亮值,求出每次修改后树的权值。

  \(n,m\le10^5\)\(c_i\le n\)\(l_i<2^{20}\)

\(\mathcal{Solution}\)

  分颜色贡献,位运算拆位,基本姿势 w!

  假定树的结点全部同色且光亮值为 \(0/1\),考虑点 \(u\) 的贡献,显然先加上所有能与它异或成 \(1\) 的结点个数,再减去在子树内或在祖先上的结点个数。而从动态修改的角度,两者都能使用 BIT(树状数组)维护!

  所以,枚举每个颜色 \(c\),对于每一个 bit,维护两个 BIT,分别保存子树内颜色为 \(c\) 且当前 bit 为 \(1\) 的结点个数和祖先上颜色为 \(c\) 且当前 bit 为 \(1\) 的结点个数,处理修改就暴力除去贡献再更新贡献即可。

  复杂度 \(\mathcal O(20(n+m)\log n)\)

\(\mathcal{Code}\)

#include <cstdio>
#include <vector>
#include <iostream>

typedef long long LL;

inline char fgc () {
	static char buf[1 << 17], *p = buf, *q = buf;
	return p == q && ( q = buf + fread ( p = buf, 1, 1 << 17, stdin ), p == q ) ? EOF : *p ++;
}

inline int rint () {
	int x = 0; char s = fgc ();
	for ( ; s < '0' || '9' < s; s = fgc () );
	for ( ; '0' <= s && s <= '9'; s = fgc () ) x = x * 10 + ( s ^ '0' );
	return x;
}

inline void wint ( const LL x ) {
	if ( 9 < x ) wint ( x / 10 );
	putchar ( x % 10 ^ '0' );
}

const int MAXN = 1e5;
int n, m, ecnt, head[MAXN + 5], col[MAXN + 5], val[MAXN + 5];
int dfc, dep[MAXN + 5], dfn[MAXN + 5], siz[MAXN + 5];
LL ans[MAXN + 5];

struct Edge { int to, nxt; } graph[MAXN * 2 + 5];

struct BinaryIndexTree {
	int val[MAXN + 5];
	inline int lowbit ( const int x ) { return x & -x; }
	inline void update ( int x, const int k ) { for ( ; x <= n; x += lowbit ( x ) ) val[x] += k; }
	inline int sum ( int x ) { int ret = 0; for ( ; x; x -= lowbit ( x ) ) ret += val[x]; return ret; }
	inline int sum ( const int l, const int r ) { return sum ( r ) - sum ( l - 1 ); }
} facnt, soncnt, fabit[20], sonbit[20];

struct BitBucket {
	int all, cnt[20];
	inline void clear () { all = 0; for ( int i = 0; i < 20; ++ i ) cnt[i] = 0; }
	inline void update ( const int x, const int k ) {
		all += k;
		for ( int i = 0; 1 << i <= x; ++ i ) {
			if ( ( x >> i ) & 1 ) {
				cnt[i] += k;
			}
		}
	}
	inline LL query ( const int x ) {
		LL ret = 0;
		for ( int i = 0; i < 20; ++ i ) {
			if ( ( x >> i ) & 1 ) ret += 1ll * ( all - cnt[i] ) << i;
			else ret += 1ll * cnt[i] << i;
		}
		return ret;
	}
} buc;

struct Event {
	int u, val, time, type;
	Event () {}
	Event ( const int tu, const int tv, const int tti, const int tty ):
		u ( tu ), val ( tv ), time ( tti ), type ( tty ) {}
}; std::vector<Event> evt[MAXN + 5];

inline void link ( const int s, const int t ) {
	graph[++ ecnt].to = t, graph[ecnt].nxt = head[s];
	head[s] = ecnt;
}

inline void init ( const int u, const int f ) {
	siz[u] = 1, dfn[u] = ++ dfc, dep[u] = dep[f] + 1;
	for ( int i = head[u], v; i; i = graph[i].nxt ) {
		if ( ( v = graph[i].to ) ^ f ) {
			init ( v, u ), siz[u] += siz[v];
		}
	}
}

inline void initEvent () {
	m = rint ();
	for ( int i = 1; i <= n; ++ i ) evt[col[i]].push_back ( Event ( i, val[i], 0, 1 ) );
	for ( int i = 1, op, u, t; i <= m; ++ i ) {
		op = rint (), u = rint (), t = rint ();
		evt[col[u]].push_back ( Event ( u, val[u], i, 0 ) );
		if ( op & 1 ) evt[col[u] = t].push_back ( Event ( u, val[u], i, 1 ) );
		else evt[col[u]].push_back ( Event ( u, val[u] = t, i, 1 ) );
	}
	for ( int i = 1; i <= n; ++ i ) evt[col[i]].push_back ( Event ( i, val[i], m + 1, 0 ) );
}

inline LL queryFa ( const int u, const int val ) {
	LL ret = 0;
	int all = facnt.sum ( dfn[u] );
	for ( int i = 0; i < 20; ++ i ) {
		if ( ( val >> i ) & 1 ) ret += 1ll * ( all - fabit[i].sum ( dfn[u] ) ) << i;
		else ret += 1ll * fabit[i].sum ( dfn[u] ) << i;
	}
	return ret;
}

inline void updateFa ( const int u, const int val, const int k ) {
	int l = dfn[u], r = dfn[u] + siz[u];
	facnt.update ( l, k ), facnt.update ( r, -k );
	for ( int i = 0; 1 << i <= val; ++ i ) {
		if ( ( val >> i ) & 1 ) {
			fabit[i].update ( l, k );
			fabit[i].update ( r, -k );
		}
	}
}

inline LL querySon ( const int u, const int val ) {
	LL ret = 0;
	int l = dfn[u], r = dfn[u] + siz[u] - 1;
	int all = soncnt.sum ( l, r );
	for ( int i = 0; i < 20; ++ i ) {
		if ( ( val >> i ) & 1 ) ret += 1ll * ( all - sonbit[i].sum ( l, r ) ) << i;
		else ret += 1ll * sonbit[i].sum ( l, r ) << i;
	}
	return ret;
}

inline void updateSon ( const int u, const int val, const int k ) {
	soncnt.update ( dfn[u], k );
	for ( int i = 0; 1 << i <= val; ++ i ) {
		if ( ( val >> i ) & 1 ) {
			sonbit[i].update ( dfn[u], k );
		}
	}
}

int main () {
	freopen ( "cop.in", "r", stdin );
	freopen ( "cop.out", "w", stdout );
	n = rint ();
	for ( int i = 1; i <= n; ++ i ) col[i] = rint ();
	for ( int i = 1; i <= n; ++ i ) val[i] = rint ();
	for ( int i = 1, u, v; i < n; ++ i ) {
		u = rint (), v = rint ();
		link ( u, v ), link ( v, u );
	}
	init ( 1, 0 ), initEvent ();
	for ( int c = 1; c <= n; ++ c ) {
		buc.clear ();
		LL sum = 0, ill = 0;
		for ( int i = 0; i ^ evt[c].size (); ++ i ) {
			Event cur ( evt[c][i] );
			if ( cur.type ) {
				sum += buc.query ( cur.val ), buc.update ( cur.val, 1 );
				ill += queryFa ( cur.u, cur.val ), updateFa ( cur.u, cur.val, 1 );
				ill += querySon ( cur.u, cur.val ), updateSon ( cur.u, cur.val, 1 );
			} else {
				sum -= buc.query ( cur.val ), buc.update ( cur.val, -1 );
				ill -= queryFa ( cur.u, cur.val ), updateFa ( cur.u, cur.val, -1 );
				ill -= querySon ( cur.u, cur.val ), updateSon ( cur.u, cur.val, -1 );
			}
			if ( cur.time <= m ) {
 				ans[cur.time] += sum - ill;
				ans[i + 1 == ( int ) evt[c].size () ? m + 1 : evt[c][i + 1].time] -= sum - ill;
			}
		}
	}
	wint ( ans[0] ), putchar ( '\n' );
	for ( int i = 1; i <= m; ++ i ) printf ( "%lld", ans[i] += ans[i - 1] ), putchar ( '\n' );
	return 0;
}

\(\mathcal{Details}\)

  考场上卡常 \(\mathcal O(n(m+\log n))\) 骗到 \(50pts\) 心满意足 www。

  这种类似离线的处理方法要感知到位,毕竟兔子这种码力持久化啊虚树啊都不可能考场敲出来 qwq(自暴自弃。

posted @ 2020-08-23 21:24  Rainybunny  阅读(156)  评论(0编辑  收藏  举报