[SDOI2017] 切树游戏 (FWT)(动态DP)(全局平衡二叉树)

传送门

题意:求一棵树的所有权值异或起来为 K K 的联通块个数,单点修改, n , q 3 e 4 , a i < 128 n,q\le3e4,a_i<128

先考虑朴素的 d p dp

f [ u ] [ j ] f[u][j] 表示 u u 的子树,强制选 u u ,异或起来为 j j 的方案数

f [ u ] [ j ] = x y = j f [ u ] [ x ] f [ v ] [ y ] f[u][j]=\prod_{x\bigotimes y=j} f[u][x]*f[v][y]

直接先 f w t fwt 全部变成点乘,最后再还原

然后还要求一个子树的前缀和,不妨令为 g [ u ] [ j ] g[u][j]

直接上链分治,定义 f , g f',g' 表示轻儿子和自己和起来的 d p dp

f [ u ] [ j ] = ( f [ v ] [ j ] + 1 ) f'[u][j]=\prod(f[v][j]+1)
g [ u ] [ j ] = g [ v ] [ j ] + f [ u ] [ j ] g'[u][j]=\sum g[v][j]+f[u][j]

于是有转移

f [ u ] [ j ] = f [ u ] [ j ] ( f [ u + 1 ] [ j ] + 1 ) f[u][j]=f'[u][j]*(f[u+1][j]+1)
g [ u ] [ j ] = g [ u ] [ j ] + g [ u + 1 ] [ j ] + f [ u ] [ j ] ( f [ u + 1 ] [ j ] + 1 ) g[u][j]=g'[u][j]+g[u+1][j]+f'[u][j]*(f[u+1][j]+1)

写成矩阵乘法的形式

[ f [ u ] [ j ] g [ u ] [ j ] 1 ] = [ f [ u ] [ j ] f [ u ] [ j ] 0 0 1 0 f [ u ] [ j ] f [ u ] [ j ] + g [ u ] [ j ] 1 ] [ f [ u + 1 ] [ j ] g [ u + 1 ] [ j ] 1 ] \begin{bmatrix} f[u][j] \\ g[u][j] \\1\end{bmatrix}=\begin{bmatrix} f'[u][j] & f'[u][j] & 0\\ 0 & 1 & 0\\f'[u][j] & f'[u][j]+g'[u][j] & 1 \end{bmatrix}\begin{bmatrix} f[u+1][j]\\ g[u+1][j] \\ 1 \end{bmatrix}

把中间 4 个关键点单独算就可以只有 4 倍小常数

关于全局平衡二叉树:

对每一条重链建一棵二叉树,然后一条重链的二叉树的根连向轻边的 f a fa

直接建深度是 l o g ( n ) 2 log(n)^2

我们对重链建的时候不从中间剖,我们对除重儿子之外的 s i z e size 从中间分

这样做是 l o g ( n ) log(n)

每次修改的时候暴力跳 f a fa ,然后 p u s h u p pushup ,如果切轻链的就要重新算一下

有一个头疼的是重新算的时候有可能除以 0,所以要记录一下 0 的个数,将一个数表示成 x 0 y x*0^y 的形式

#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int N = 3e4 + 5, M = 130;
cs int Mod = 1e4 + 7, inv2 = (Mod+1)>>1;
int read(){
	int cnt = 0, f = 1; char ch = 0;
	while(!isdigit(ch)){ ch = getchar(); if(ch == '-') f = -1; }
	while(isdigit(ch)) cnt = cnt*10 + (ch-'0'), ch = getchar();
	return cnt * f;
}
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans, a); return ans; }
void Add(int &a, int b){ a = add(a, b); }
void Mul(int &a, int b){ a = mul(a, b); }
void Dec(int &a, int b){ a = dec(a, b); }
int first[N], nxt[N<<1], to[N<<1], tot;
void adde(int x, int y){ nxt[++tot] = first[x], first[x] = tot, to[tot] = y; }
int inv[Mod];
int n, S, m, vl[N], fw[M][M];
int siz[N], son[N], ans[M];
void pre_dfs(int u, int f){
	siz[u] = 1; for(int i=first[u];i;i=nxt[i]){
		int t = to[i]; if(t == f) continue; pre_dfs(t, u); 
		siz[u] += siz[t]; if(siz[t] > siz[son[u]]) son[u] = t;
	}
}
struct tag{
	int x, y;
	tag(int _x = 0, int _y = 0){ x = _x, y = _y; }
	void operator = (int v){ v ? (x = v, y = 0) : (x = y = 1); }
	void operator *= (int v){ v ? x = mul(x, v) : ++y; }
	void operator /= (int v){ v ? x = mul(x, inv[v]) : --y; }
	operator int (){ return y ? 0 : x; }	
};
struct data{
	int a[M], b[M], c[M], d[M];
	void operator = (cs data &A){
		memcpy(a, A.a, sizeof(int)*S);
		memcpy(b, A.b, sizeof(int)*S);
		memcpy(c, A.c, sizeof(int)*S);
		memcpy(d, A.d, sizeof(int)*S);
	}
};
void Inc(data &C, data A, data B){
	for(int i = 0; i < S; i++){
		C.a[i] = mul(A.a[i], B.a[i]);
		C.b[i] = add(mul(A.a[i], B.b[i]), A.b[i]);
		C.c[i] = add(mul(A.c[i], B.a[i]), B.c[i]);
		C.d[i] = add(mul(A.c[i], B.b[i]), add(A.d[i], B.d[i]));
	}
}
void FWT(int *a, int typ){
	for(int i = 1; i < S; i <<= 1)
	for(int j = 0; j < S; j += (i<<1))
	for(int k = 0; k < i; k++){
		int x = a[k + j], y = a[k + j + i];
		a[k + j] = add(x, y); a[k + j + i] = dec(x, y);
		if(typ == -1) Mul(a[k + j], inv2), Mul(a[k + j + i], inv2);
	}
}
namespace GBT{
	int rt, cn[N], tp;
	int lsz[N], ch[N][2], fa[N];
	data w[N], a[N];
	tag F[N][M]; int G[N][M];
	void init_node(int x){
		int *W = fw[vl[x]]; data &A = w[x];
		for(int i = 0; i < S; i++){
			A.a[i] = mul(F[x][i], W[i]);
			A.d[i] = add(A.a[i], G[x][i]);
		} 
		memcpy(A.b, A.a, sizeof(int)*S);
		memcpy(A.c, A.a, sizeof(int)*S);
	}
	void pushup(int x){
		Inc(a[x], a[ch[x][0]], w[x]);
		Inc(a[x], a[x], a[ch[x][1]]);
	}
	void ins(int u, int v){
		int *t1 = a[v].c, *t2 = a[v].d;
		for(int i = 0; i < S; i++){
			F[u][i] *= add(t1[i], 1);
			Add(G[u][i], t2[i]);
		}
	}
	void del(int u, int v){
		int *t1 = a[v].c, *t2 = a[v].d;
		for(int i = 0; i < S; i++){
			F[u][i] /= add(t1[i], 1);
			Dec(G[u][i], t2[i]);
		}
	}
	int subbuild(int l, int r){
		if(l == r){ pushup(cn[l]); return cn[l]; } if(l > r) return 0;
		int tot = 0; for(int i = l; i <= r; i++) tot += lsz[cn[i]];
		for(int i = l, now = lsz[cn[l]]; i <= r; now += lsz[cn[++i]])
		if(now * 2 >= tot){
			ch[cn[i]][0] = subbuild(l, i-1);
			ch[cn[i]][1] = subbuild(i+1, r);
			fa[ch[cn[i]][0]] = fa[ch[cn[i]][1]] = cn[i]; pushup(cn[i]);
			return cn[i];
		}
	}
	int build(int u){
		for(int p = u; p; p = son[p]) lsz[p] = siz[p] - siz[son[p]];
		for(int p = u; p; p = son[p])
		for(int e = first[p]; e; e = nxt[e]){
			int t = to[e]; if(!lsz[t]){ t = build(t); fa[t] = p; ins(p, t); }
		} tp = 0;
		for(int p = u; p; p = son[p]) cn[++tp] = p, init_node(p);
		reverse(cn + 1, cn + tp + 1); 
		return subbuild(1, tp);
	}
	bool hv;
	void Get(){
		if(hv) return; hv = true;
		memcpy(ans, a[rt].d, sizeof(int)*S); FWT(ans, -1);
	}
	bool isroot(int x){ return ch[fa[x]][0] != x && ch[fa[x]][1] != x; }
	void modify(){
		hv = false;
		int u = read(); vl[u] = read(); init_node(u);
		while(u){
			if(isroot(u) && fa[u]){
				del(fa[u], u); pushup(u); ins(fa[u], u); init_node(fa[u]);
			} else pushup(u); //cout << "modify "<<u << endl;debug(a[u].d); 
			u = fa[u];
		}
	}
	void init(){
		for(int i = 1; i <= n; i++)
		for(int j = 0; j < S; j++) F[i][j] = 1;
		for(int i = 0; i < S; i++) w[0].a[i] = a[0].a[i] = 1;
		rt = build(1);
	}
}
int main(){
	inv[0] = inv[1] = 1;
	for(int i = 2; i < Mod; i++) inv[i] = mul(Mod-Mod/i, inv[Mod%i]);
	n = read(), S = read();
	for(int i = 1; i <= n; i++) vl[i] = read();
	for(int i = 1; i < n; i++){ int x = read(), y = read(); adde(x, y); adde(y, x); }
	for(int i = 0; i < S; i++) fw[i][i] = 1, FWT(fw[i], 1);
	pre_dfs(1, 0); GBT::init();
	m = read();
	while(m--){
		char opt[10]; scanf("%s", opt);
		if(opt[0] == 'Q'){ GBT::Get(); cout << ans[read()] << '\n'; }
		if(opt[0] == 'C') GBT::modify();
	} return 0;
}
发布了610 篇原创文章 · 获赞 94 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sslz_fsy/article/details/103428094