首页 > 其他分享 >洛谷 P9745 「KDOI-06-S」树上异或 题解

洛谷 P9745 「KDOI-06-S」树上异或 题解

时间:2024-01-19 19:24:13浏览次数:23  
标签:P9745 连通 06 int 题解 REP times 异或 断边

Solution

树形 DP 好题。

Part I 部分分类比

下文为简单,我们称一个连通块的权值为连通块内点的异或和。

考虑链的部分分,显然可以设 \(f_{i}\) 是前 \(i\) 个点所有断边方案的权值和,对于每个点枚举上一条断的边转移。令 \(s_i=\bigoplus_{j=1}^{i}a_j\),则 \(f_i=\sum_{j=0}^{i-1}(s_i\oplus s_{j})\times f_j\)。

这样是 \(\mathcal{O}(n^2)\) 的,我们拆位算贡献,就可以做到 \(\mathcal{O}(n\log V)\)。

具体地,设 \(g_{i,j,k}\) 是所有断边方案中,与 \(i\) 相连的连通块的价值在二进制下第 \(j\) 位是 \(k\) 的,不与 \(i\) 相连的连通块的价值乘积的和。

初始状态:若 \(a_u\) 第 \(j\) 位为 \(1\),则 \(g_{i,j,1}=1\),否则 \(g_{i,j,0}=1\)。

转移如下:

  • 为了避免后效性,先保存 \(t_0=g_{i,j,0},t_1=g_{i,j,1}\)。
  • \(\begin{cases} g_{i,j,0}=t_{0}\times(g_{i-1,j,0}+f_{i-1})+t_{1}\times g_{i-1,j,1}\\ g_{i,j,1}=t_{0}\times g_{i-1,j,1}+t_{1}\times (g_{i-1,j,0}+f_{i-1})\\ f_{i}=\sum_{j=0}^{63}2^j\times g_{i,j,1} \end{cases}\)

同理,对于树我们也通过断边来转移。

Part II 解决问题

设 \(f_{u}\) 是以 \(u\) 为根的子树的所有断边方案的权值和。

为了转移,再设 \(g_{u,i,j}\) 是以 \(u\) 为根的子树里断开若干边,所有断边方案中,与 \(u\) 相连的连通块的价值在二进制下第 \(i\) 位是 \(j\) 的,不与 \(u\) 相连的连通块的价值乘积的和。

初始状态:若 \(a_u\) 第 \(i\) 位为 \(1\),则 \(g_{u,i,1}=1\),否则 \(g_{u,i,0}=1\)。

对于每个儿子,枚举二进制下每位 \(i\) 转移。

  • 不断儿子相当于当前连通块的异或和第 \(i\) 位异或上儿子连通块的第 \(i\) 位,断掉儿子相当于异或上 \(0\)。
  • 为了避免后效性,先保存 \(t_0=g_{u,i,0},t_1=g_{u,i,1}\)。
  • \(\begin{cases} g_{u,i,0}\gets t_0\times(g_{v,i,0}+f_{v})+t_1\times g_{v,i,1} \\ g_{u,i,1}\gets t_0\times g_{v,i,1}+t_1\times(g_{v,i,0}+f_{v}) \end{cases}\)

转移完后,\(f_{u}\) 就可以简单地计算啦。

  • \(f_{u}=\sum_{i=0}^{63}2^i\times g_{u,i,1}\)。

答案显然就是 \(f_{1}\)。

Part III 小结

不难发现该算法时空复杂度均为 \(\mathcal{O}(n\log V)\),需要将 \(g_{u,i,j}\) 用 int 类型保存才能通过。

启示:

  • 树形 DP 的题可以用链的部分分类比得出正解。
  • 树形 DP 需要想清楚状态、列明白转移方程再写,否则在调试过程中通常会越写越复杂,导致耗费大量时间而最终一分也不得。

Code

#include <bits/stdc++.h>
#define REP(i, l, r) for (int i = (l); i <= (r); ++ i)
#define DEP(i, r, l) for (int i = (r); i >= (l); -- i)
#define fi first
#define se second
using namespace std;
namespace Milkcat {
    typedef long long LL;
    typedef pair<LL, LL> pii;
    const int N = 1e6 + 5, mod = 998244353;
    LL n, u, v, a[N], f[N]; int g[N][64][2];
    vector<int> G[N];
    void dfs(int u, int fa) {
    	REP(i, 0, 63) g[u][i][a[u] >> i & 1] = 1;
    	for (int v : G[u]) {
    		if (v == fa) continue;
    		dfs(v, u);
    		REP(i, 0, 63) {
    			LL t0 = g[u][i][0], t1 = g[u][i][1];
    			g[u][i][0] = (t0 * (g[v][i][0] + f[v]) + t1 * g[v][i][1]) % mod;
    			g[u][i][1] = (t0 * g[v][i][1] + t1 * (g[v][i][0] + f[v])) % mod;
    		}
		}
		REP(i, 0, 63) f[u] = (f[u] + (1LL << i) % mod * g[u][i][1]) % mod;
	}
    int main() {
		cin >> n;
		REP(i, 1, n) cin >> a[i];
		REP(i, 2, n)
			cin >> u, G[u].push_back(i), G[i].push_back(u);
		dfs(1, 0);
		cout << f[1] << '\n';
        return 0;
    }
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
    int T = 1;
    while (T --) Milkcat::main();
    return 0;
}

标签:P9745,连通,06,int,题解,REP,times,异或,断边
From: https://www.cnblogs.com/Milkcatqwq/p/17975419

相关文章

  • 洛谷 P9579「Cfz Round 1」Elevator 另类题解
    一个赛时想到的另类DP做法。Solution容易将原题转化为一个人乘电梯每次上下一层。对于\(a_i<b_i\)是好处理的,记\(\displaystylem=\max_{1\leqi\leqn}\{a_i,b_i\}\),只需要跑到\(m\)即可解决所有这种条件。对于\(a_i>b_i\)的条件,我们除了到\(m\)外,还需要额外地从......
  • 洛谷 P9575 「TAOI-2」喵了个喵 Ⅳ 题解
    Solution先求出所有数的最大公约数\(d\),然后将每个数约去\(d\)。将约去后的数均分,约去前的数也均分。下文讨论的数都是约去\(d\)后的数(包括取的\(x\))。\(n\)为偶数,取\(x=1\),对半分即可。\(n\)不为偶数,且有奇数个偶数。取\(x=2\),设奇数和偶数分别有\(x,y\)个,B组取......
  • 洛谷 P9915 「RiOI-03」3-2 题解
    Preface为啥有蓝啊,这题在机房里15min左右就切了,反倒是2A做了1h。。Solution将矩阵逆时针旋转\(90^{\circ}\),你会发现这是一棵线段树,是父亲左儿子的节点颜色是\(0\),是右儿子的节点颜色是\(1\)。容易发现,联通块一定是一条链。具体地,你从给定的点向上跳,跳到第一个与自己......
  • CF1895E Infinite Card Game 题解
    Solution根据贪心策略,可以发现出完一张牌后对手的出牌是固定的。同理可以算出Monocarp出完一张牌\(a\)后下一次出的牌\(to_a\)。\(a\)和\(to_a\)胜负状态相同。可以发现对所有\(a\)建\(a\toto_a\)后形成的图是内向基环树,一遍dfs即可求出答案。时间复杂度\(\m......
  • /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found问题解决
    有一个go实现的项目代码最近有更新,自己在开发环境上手动构建并运行都没有问题(构建和运行时相同环境,肯定没有问题^_^)。后面通过jenkins构建镜像也没有问题,运行时却报错 之前的版本在jenkins上构建也是成功的,后沟通得知jenkins集群版本最近有更新 那么,大概知道原因了,由于jenk......
  • CF1214E题解
    PetyaandConstructionSet题目传送门题解一个构造题,结论挺容易猜的。观察到关键信息:\(d_i\len\)。所以我们先把所有奇数编号的点按对应的\(d\)从大到小组成一条链,然后依次考虑偶数号点应该接在链上的哪个点后,容易知道这个点为链上的第\(i+d-1\)个。特殊的,如果接在了最后......
  • CF150C题解
    SmartCheater题目传送门题解首先显然的,每个乘客是独立计算的,然后我们发现,一个乘客在\(i\)到\(i+1\)不买票的期望贡献是一定的,为\(\dfrac{x_{i+1}-x_i}{2}-c*p_i\),所以我们其实就是要对于每个乘客的区间求最大子段和,简单线段树板子,感觉也没啥细节。代码:#include<bits/st......
  • CF1286C1题解
    Madhouse(Easyversion)题目传送门题解这种水题还能有蓝?不能因为困难版是黑就把简单版难度往上强拉啊!第一次问\([1,n]\),第二次问\([1,n-1]\),把读入的所有字符串先各自内部把字符排序(反正本来就是乱序)后存入map,第一次询问有,第二次询问没有的字符串就是原串后缀的乱序,都找出......
  • NOIP2023题解
    目录NOIP2023T1词典(dict)T2三值逻辑(tribool)T3双序列拓展(expand)T4天天爱打卡(run)NOIP2023T1词典(dict)考察:贪心题解Link题目传送门首先任意多次操作本质就是随意排序,所以如果要使\(w_i\)最小,我们一定会使\(w_i\)从\(a\)到\(z\)排,其它都\(z\)到\(a\)排......
  • CF1899G题解
    UnusualEntertainment题目传送门题解真的不要太典,,,考虑点\(u\)是\(v\)的后代等价于\(u\)在子树\(v\)中,dfs序直接走起,变成判断是否存在\(i\)使得:\[in_x\lein_{p_i}\leout_x,l\lei\ler\]二维数点,启动!代码:#include<bits/stdc++.h>usingnamespacestd;#defi......