[日常训练]Hard(组合数学 + FMT)

Meaning

  • 有一棵 n n n 个点的树,根结点为 1 1 1 号结点,初始时每个结点有一个值 a i a_i ai
  • 每一轮,你需要从根结点开始,将每个结点的权值更新为它子树中所有结点上一轮结束后权值的异或和
  • q q q 个询问,每个询问给定轮数 T T T ,你需要输出 T T T 轮后根结点的值
  • 0 ≤ T , a i ≤ 1 0 18 , 1 ≤ n , q ≤ 1 0 6 0\le T,a_i\le10^{18},1\le n,q\le10^6 0T,ai1018,1n,q106

Solution

  • 好题
  • 先特判掉 T = 0 T=0 T=0
  • 可以发现 T T T 轮操作之后根节点的值的组合意义
  • 即对于一个点 u u u ,如果从点 u u u 开始,跳 T T T 步(每步只能跳向自己及其祖先)到达根的方案数为奇数,则 a u a_u au 为答案以异或的方式贡献,否则不贡献
  • 这个组合意义可以通过归纳等手段得出
  • 根据组合数学插板法,我们易得点 u u u T T T 步跳到根的方案数为 ( d e p u + T − 1 T − 1 ) \binom{dep_u+T-1}{T-1} (T1depu+T1) d e p u dep_u depu 为点 u u u 的深度,根的深度为 0 0 0
  • 根据 Lucas 定理,我们可以判断 ( a b ) \binom ab (ba) 的奇偶性
  • 即对于任何一个 i i i ,都不满足「 a a a 的第 i i i 个二进制位为 0 0 0 b b b 的第 i i i 个二进制位为 1 1 1
  • 即二进制意义下, b b b 1 1 1 的位是 a a a 1 1 1 的位的子集
  • 我们继续研究 ( d e p u + T − 1 T − 1 ) ≡ 1 (   m o d   2 ) \binom{dep_u+T-1}{T-1}\equiv1(\bmod 2) (T1depu+T1)1(mod2) 的条件
  • T − 1 T-1 T1 中为 1 1 1 的位是 d e p u + T − 1 dep_u+T-1 depu+T1 中为 1 1 1 的位的子集的条件
  • 容易发现,如果我们能够找到一个最低的位 i i i 满足 T − 1 T-1 T1 的第 i i i 位和 d e p u dep_u depu 的第 i i i 位都为 1 1 1 ,那么 d e p u + T − 1 dep_u+T-1 depu+T1 的第 i i i 位必然因为进位而变成 0 0 0 ,这时候显然就不满足条件了
  • 于是 T − 1 T-1 T1 中为 1 1 1 的位是 d e p u + T − 1 dep_u+T-1 depu+T1 中为 1 1 1 的位的子集,当且仅当 T − 1 T-1 T1 中为 1 1 1 的位与 d e p u dep_u depu 中为 1 1 1 的位没有交集
  • d e p u dep_u depu T − 1 T-1 T1补集的子集
  • s u m i sum_i sumi 表示深度为 i i i 的点的权值异或和
  • 那么一次询问相当于求
  • ⨁ i ⊂ S s u m i \bigoplus_{i\subset S}sum_i iSsumi
  • S S S 表示 T − 1 T-1 T1 各位取反之后得到的数, i ⊂ S i\subset S iS 表示 i i i 中为 1 1 1 的位是 S S S 中为 1 1 1 的位的子集
  • 可以用 FMT O ( n log ⁡ n ) O(n\log n) O(nlogn) 预处理后 O ( 1 ) O(1) O(1) 回答
  • O ( n log ⁡ n + q ) O(n\log n+q) O(nlogn+q)

Code

#include <bits/stdc++.h>

// 0830

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

typedef long long ll;

inline ll readll()
{
	ll res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

const int N = 11e5 + 5, M = N << 1;

int n, q, ecnt, nxt[M], adj[N], go[M], ff = 1, tot;
ll T, a[N], sum[N];

void add_edge(int u, int v)
{
	nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
	nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}

void dfs(int u, int dep, int fu)
{
	sum[dep] ^= a[u];
	for (int e = adj[u], v; e; e = nxt[e])
		if ((v = go[e]) != fu) dfs(v, dep + 1, u);
}

int main()
{
	#ifdef orzInvUsr
	#else
		freopen("hard.in", "r", stdin);
		freopen("hard.out", "w", stdout);
	#endif
	
	int x, y;
	n = read(); q = read();
	for (int i = 1; i < n; i++)
		x = read(), y = read(), add_edge(x, y);
	for (int i = 1; i <= n; i++) a[i] = readll();
	dfs(1, 0, 0);
	while (ff < n) ff <<= 1, tot++;
	for (int i = 0; i < tot; i++)
		for (int j = 0; j < ff; j += (1 << i + 1))
			for (int k = 0; k < (1 << i); k++)
				sum[j + (1 << i) + k] ^= sum[j + k];
	while (q--)
	{
		T = read();
		if (!T) {printf("%lld\n", a[1]); continue;}
		T--;
		printf("%lld\n", sum[ff - 1 ^ (T & ff - 1)]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值