bzoj3696 化合物

mac2022-06-30  25

传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3696

【题解】

f[x][i]表示x节点子树,值为i的方案数。

暴力统计理论复杂度$O(nH^2)$。

按子树排序后统计复杂度好像还是错的。。但是能过

明天研究FWT。。

暴力跑了0.8s,FWT跑了3.1s。。。日

FWT也要按深度合并啊。。FWT的介绍在最后

# include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10, N = 1e5 + 10, H = 512 + 5; const int mod = 1e9+7; int n, f[N][H], g[N], par[N]; ll ans[H]; int t[N], tn; inline int cmp(int a, int b) { return g[a] < g[b]; } int head[N], nxt[N], to[N], tot = 0; inline void add(int u, int v) { ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v; } ll ss[H], tt[H]; inline void FWT(ll *a, int op, int L) { if(op) { for (int len=2; len<=L; len<<=1) { int m = len >> 1; for (ll *p = a; p != a+L; p += len) { for (int k=0; k<m; ++k) { ll x = p[k], y = p[k+m]; p[k] = (x+y)/2; p[k+m] = (x-y)/2; } } } } else { for (int len=2; len<=L; len<<=1) { int m = len >> 1; for (ll *p = a; p != a+L; p += len) { for (int k=0; k<m; ++k) { ll x = p[k], y = p[k+m]; p[k] = x+y; p[k+m] = x-y; } } } } } inline void FWT_combine(int *A, int an, int *B, int bn) { int L = 0, m = max(an, bn); while((1<<L) < m) ++L; m = (1<<L); for (int i=0; i<m; ++i) ss[i] = A[i], tt[i] = B[i]; FWT(ss, 0, m), FWT(tt, 0, m); for (int i=0; i<m; ++i) ss[i] = ss[i] * tt[i]; FWT(ss, 1, m); for (int i=0; i<m; ++i) ans[i] += ss[i]; } inline void dfs(int x) { int ret = 1; f[x][0] = 1; for (int i=head[x]; i; i=nxt[i]) dfs(to[i]); tn = 0; for (int i=head[x]; i; i=nxt[i]) t[++tn] = to[i]; sort(t+1, t+tn+1, cmp); for (int i=1; i<=tn; ++i) { int h = g[t[i]], y = t[i]; FWT_combine(f[x], ret, f[t[i]], h); for (int j=0; j<h; ++j) f[x][j] += f[y][j]; ret = max(ret, h); } for (int j=ret; j; --j) f[x][j] = f[x][j-1]; f[x][0] = 0; g[x] = ret + 1; } int main() { cin >> n; for (int i=2; i<=n; ++i) scanf("%d", par+i), add(par[i], i); dfs(1); for (int i=511; ~i; --i) if(ans[i] != 0) { for (int j=0; j<=i; ++j) printf("%lld\n", ans[j]); return 0; } return 0; } View Code

下面是暴力:

# include <stdio.h> # include <string.h> # include <iostream> # include <algorithm> // # include <bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; const int M = 5e5 + 10, N = 1e5 + 10, H = 512 + 5; const int mod = 1e9+7; int n, f[N][H], g[N], par[N]; ll ans[H]; int t[N], tn; inline int cmp(int a, int b) { return g[a] < g[b]; } int head[N], nxt[N], to[N], tot = 0; inline void add(int u, int v) { ++tot; nxt[tot] = head[u]; head[u] = tot; to[tot] = v; } inline void dfs(int x) { int ret = 1; f[x][0] = 1; for (int i=head[x]; i; i=nxt[i]) dfs(to[i]); tn = 0; for (int i=head[x]; i; i=nxt[i]) t[++tn] = to[i]; sort(t+1, t+tn+1, cmp); for (int i=1; i<=tn; ++i) { int h = g[t[i]], y = t[i]; for (int j=0; j<ret; ++j) for (int k=0; k<h; ++k) ans[j ^ k] += 1ll * f[x][j] * f[y][k]; for (int j=0; j<h; ++j) f[x][j] += f[y][j]; ret = max(ret, h); } for (int j=ret; j; --j) f[x][j] = f[x][j-1]; f[x][0] = 0; g[x] = ret + 1; } int main() { cin >> n; for (int i=2; i<=n; ++i) scanf("%d", par+i), add(par[i], i); dfs(1); for (int i=511; ~i; --i) if(ans[i] != 0) { for (int j=0; j<=i; ++j) printf("%lld\n", ans[j]); return 0; } return 0; } View Code

===================================分割线==============================

考虑集合的位运算卷积$C_i = \sum_{j \oplus k = i}A_jB_k$

每次只需要考虑最高位,事先补齐集合至2的次幂。设符号$C = (A, B)$表示$C$由$A$和$B$顺次拼接而成。

如果只考虑最高位的话,有:$ C = A \oplus B = (\sum_{j \oplus k = 0} A_jB_k, \sum_{j \oplus k = 1} A_jB_k)$(这里的0和1是因为值考虑最高位的原因)

这给了我们一个非常好的分治想法假设我们构造出变换$f$以及逆变换$f_T$,满足:$f(A)f(B) = f(C)$那么我们就用类似于FFT的方法实现即可。

1. 如果是异或(xor)卷积的话:$A_0$和$A_1$是按照$A$的最高位分成的两部分$f(A) = (f(A_0) + f(A_1), f(A_0) - f(A_1))$$f_T(A) = (f_T(\frac{A_0 + A_1}{2}), f_T(\frac{A_0 - A_1}{2}))$正确性可以用归纳法证明。其实只要记住正变换的式子即可,逆变换可以从正变换推导出来。

2. 如果是与(and)卷积的话$f(A) = (f(A_0) + f(A_1), f(A_1))$$f_T(A) = (f_T(A_0) - f_T(A_1), f_T(A_1))$

3. 如果是或(or)卷积的话$f(A) = (f(A_0), f(A_1) + f(A_0))$$f_T(A) = (f_T(A_0), f_T(A_1) - f_T (A_0))$

转载于:https://www.cnblogs.com/galaxies/p/bzoj3696.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)