切树游戏
题目大意
给你一棵树,会有单点修改,要你在其中求有多少棵子树的权值异或和是一个询问的 k。
思路
首先考虑没有修改的 DP。
先是最暴力的:fi,j 为 i 的子树,当前选的子树有 i,异或和为 j。
询问答案 k 就是 n∑i=1fk,j。
然后转移是枚举子树 son:fi,j=∑x⊕y=jfi,xfson,y+fi,j。
然后这个显然是可以用 FWT 优化的:nfi=FWT[ai]
(其实卷积起来就是 fi,k=nfi,k∏son(fson,k+1))
然后至于答案我们可以搞 hi,k=fi,k+∑sonhson,k。
那我们这个就可以搞,然后搞出来最后 IFWT 转回去。
那一次的复杂度是 O(nmlogm)。
考虑加上动态改点,发现上面的 DP 其实还算简单,我们考虑 DDP。
那还是轻重链剖分,然后重儿子是 hsonx。
那我们设 lfx,k=nfx,k∏son∧son≠hsonx(fson,k+1)
lhx,k=∑son∧son≠hsonxlhson,k
然后你会发现这个 k 一直只跟自己有关联,然后一直挂在这里很烦,那我们考虑把上面转移那些去掉 k 的一维,然后把它们当做数组转移。
(因为你都跟自己有关,所以加减乘除都是 O(m) 的,就不用卷积)
然后看看怎么转移,准备上矩阵。
fx=lfx(fhsonx+1)
hx=fx+lhx+hhsonx=lfx(fhsonx+1)+lhx+hhsonx
然后你就可以尝试建矩阵啦!
先搞一个 ∣∣
∣∣fxhx1∣∣
∣∣
然后要通过乘一个矩阵把 ∣∣
∣
∣∣fhsonxhhsonx1∣∣
∣
∣∣ 变成 ∣∣
∣∣fxhx1∣∣
∣∣。
然后可以构造出:
∣∣
∣∣lfx0lfxlfx1lfx+lhx001∣∣
∣∣∗∣∣
∣
∣∣fhsonxhhsonx1∣∣
∣
∣∣=∣∣
∣∣fxhx1∣∣
∣∣
然后就可以用这个矩阵搞,复杂度为 O(qlog(2)nm∗27),好像有点大,就算用全局平衡二叉树也过不去。
然后我们看到这个矩阵的样子比较特别,考虑手玩一下:
∣∣
∣∣a10b1c11d1001∣∣
∣∣∗∣∣
∣∣a20b2c21d2001∣∣
∣∣=∣∣
∣∣a1a20a1b2+b1a2c1+c21c1b2+d2+d1001∣∣
∣∣
然后你会发现你只用维护四个值,而且它们怎么维护是固定的,所以常数就变成了 4,用平衡二叉树就可以过啦!
(好像说 luogu 树链剖分被卡了的说)
然后至于实现的话。。。多用结构体。
具体一下就是你矩阵里面每个值是数组用结构体,轻边的维护不能直接转移,因为你修改的时候要除,但是里面可能是 0,所以你要再弄一个结构体专门来轻边的转移,就是一个正常的数组加上一个表示数组每一位乘了 0 的个数。
然后你乘 0 就不乘而是加 0 的个数,除就是减,然后弄一个函数把它转回乘整除的数组,就是 O(m) 枚举一次把有 0 的变成 0。
然后建议全局平衡二叉树也可以封装一下。
(麻了代码是真的长)
代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int mo = 1e4 + 7;
const int N = 3e4 + 10;
const int M = 128;
struct node {
int to, nxt;
}e[N << 1];
int n, m, a[N], inv[N], le[N], KK;
int sz[N], son[N], ans[M];
char c;
int jia(int x, int y) {return x + y >= mo ? x + y - mo : x + y;}
int jian(int x, int y) {return x < y ? x - y + mo : x - y;}
int cheng(int x, int y) {return x * y % mo;}
void FWT(int *f, int limit, int op) {
for (int mid = 1; mid < limit; mid <<= 1) {
for (int R = mid << 1, j = 0; j < limit; j += R)
for (int k = 0; k < mid; k++) {
int x = f[j | k], y = f[j | mid | k];
f[j | k] = jia(x, y); f[j | mid | k] = jian(x, y);
if (op == -1) f[j | k] = cheng(f[j | k], inv[2]), f[j | mid | k] = cheng(f[j | mid | k], inv[2]);
}
}
}
void add(int x, int y) {e[++KK] = (node){y, le[x]}; le[x] = KK;}
void dfs(int now, int father) {
sz[now] = 1;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
dfs(e[i].to, now); sz[now] += sz[e[i].to];
if (sz[e[i].to] > sz[son[now]]) son[now] = e[i].to;
}
}
struct poly {
int f[M];
int& operator [](int& x) {return f[x];}
poly operator +(poly y) {poly re; for (int i = 0; i < m; i++) re[i] = jia(f[i], y[i]); return re;}
poly operator -(poly y) {poly re; for (int i = 0; i < m; i++) re[i] = jian(f[i], y[i]); return re;}
poly operator *(poly y) {poly re; for (int i = 0; i < m; i++) re[i] = cheng(f[i], y[i]); return re;}
poly operator /(poly y) {poly re; for (int i = 0; i < m; i++) re[i] = cheng(f[i], inv[y[i]]); return re;}
void operator +=(poly y) {for (int i = 0; i < m; i++) f[i] = jia(f[i], y[i]);}
void operator -=(poly y) {for (int i = 0; i < m; i++) f[i] = jian(f[i], y[i]);}
}one, ee[N];
struct matrix {
poly a[2][2];
poly* operator [](const int& x) {return a[x];}
matrix operator *(matrix b) {
matrix re;
re[0][0] = a[0][0] * b[0][0];
re[0][1] = a[0][0] * b[0][1] + a[0][1];
re[1][0] = b[0][0] * a[1][0] + b[1][0];
re[1][1] = a[1][0] * b[0][1] + b[1][1] + a[1][1];
return re;
}
};
struct Light {
int num0[M], val[M];
void change(int pl, int va) {
if (!va) num0[pl] = val[pl] = 1;
else num0[pl] = 0, val[pl] = va;
}
void operator *=(poly f) {
for (int i = 0; i < m; i++)
if (!f.f[i]) num0[i]++;
else val[i] = cheng(val[i], f.f[i]);
}
void operator /=(poly f) {
for (int i = 0; i < m; i++)
if (!f.f[i]) num0[i]--;
else val[i] = cheng(val[i], inv[f.f[i]]);
}
};
poly get_poly(Light &b) {
poly x; for (int i = 0; i < m; i++) x[i] = b.num0[i] ? 0 : b.val[i]; return x;
}
struct BST {
Light lf[N]; poly lh[N];
int fa[N], ls[N], rs[N], root, sta[N], ssz[N];
matrix val[N], sum[N];
bool nrt(int x) {
return ls[fa[x]] == x || rs[fa[x]] == x;
}
void Make_val(int now, int to) {
lf[now] *= (sum[to][1][0] + one);
lh[now] += sum[to][1][1];
}
void Clean_val(int now, int to) {
lf[now] /= (sum[to][1][0] + one);
lh[now] -= sum[to][1][1];
}
void Make_Val(int now) {
val[now][0][0] = val[now][0][1] = val[now][1][0] = val[now][1][1] = get_poly(lf[now]);
val[now][1][1] += lh[now];
}
void up(int now) {
sum[now] = sum[ls[now]] * val[now] * sum[rs[now]];
}
int buildT(int l, int r) {
if (l > r) return 0;
int tot = 0; for (int i = l; i <= r; i++) tot += ssz[sta[i]];
for (int i = l, now = ssz[sta[i]]; i <= r; i++, now += ssz[sta[i]])
if (now * 2 >= tot) {
ls[sta[i]] = buildT(l, i - 1); rs[sta[i]] = buildT(i + 1, r);
fa[ls[sta[i]]] = fa[rs[sta[i]]] = sta[i]; up(sta[i]); return sta[i];
}
}
int build(int now, int fr) {
for (int i = now; i; fr = i, i = son[i]) {
for (int j = le[i]; j; j = e[j].nxt)
if (e[j].to != fr && e[j].to != son[i]) {
int x = build(e[j].to, i); fa[x] = i;
Make_val(i, x);
}
Make_Val(i);
}
sta[0] = 0;
for (int i = now; i; i = son[i]) sta[++sta[0]] = i, ssz[i] = sz[i] - sz[son[i]];
reverse(sta + 1, sta + sta[0] + 1);
return buildT(1, sta[0]);
}
void Init() {
for (int i = 1; i <= n; i++)
for (int j = 0; j < m; j++)
lf[i].change(j, ee[i][j]);
val[0][0][0] = sum[0][0][0] = one;
root = build(1, 0);
}
void change(int x, int y) {
lf[x] /= ee[x];
for (int i = 0; i < m; i++) ee[x][i] = 0; a[x] = y; ee[x][a[x]] = 1;
FWT(ee[x].f, m, 1); lf[x] *= ee[x]; Make_Val(x);
for (; x; x = fa[x]) {
if (nrt(x)) up(x);
else {
Clean_val(fa[x], x); up(x); Make_val(fa[x], x); Make_Val(fa[x]);
}
}
}
void update_ans() {
for (int i = 0; i < m; i++) ans[i] = sum[root][1][1][i];
FWT(ans, m, -1);
}
}T;
void Init() {
for (int i = 0; i < m; i++) one[i] = 1;
inv[0] = inv[1] = 1; for (int i = 2; i < mo; i++) inv[i] = cheng(inv[mo % i], mo - mo / i);
}
int main() {
scanf("%d %d", &n, &m); Init();
for (int i = 1; i <= n; i++) scanf("%d", &a[i]), ee[i][a[i]] = 1, FWT(ee[i].f, m, 1);
for (int i = 1; i < n; i++) {
int x, y; scanf("%d %d", &x, &y); add(x, y); add(y, x);
}
dfs(1, 0);
T.Init();
T.update_ans();
int q; scanf("%d", &q);
while (q--) {
c = getchar(); while (c != 'C' && c != 'Q') c = getchar();
if (c == 'C') {
while (c != ' ') c = getchar();
int x, y; scanf("%d %d", &x, &y);
T.change(x, y); T.update_ans();
}
else {
while (c != ' ') c = getchar();
int x; scanf("%d", &x);
printf("%d\n", ans[x]);
}
}
return 0;
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现