题面
解题思路
我们可以通过三组特征值来代表一颗子树,从而消除前效性。
第一组是子树大小的奇偶性。
第二组是子树左右翻转后是否相同,即从最左端开始染色和从最右端开始染色是否相同。
第三组是子树从最左边开始染白色和从最右边开始染黑色是否相同。
依据这三组特征值,我们可以代表一颗子树,从而进行转移。
复杂度O(64n)。
代码
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
const int MOD = 1e9 + 7;
int n;
ll dp[N][8], fu[8];
vector<int> V[N];
void clr() {
memset(fu, 0, sizeof(fu));
}
int f(int a, int b) {
// 1.奇偶位 2.相同同性 3.相异同性
int a0 = a & 1, a1 = a & 2, a2 = a & 4;
int b0 = b & 1, b1 = b & 2, b2 = b & 4;
int c0, c1 = 0, c2 = 0;
c0 = a0 ^ b0;
if (((a1 && !b0) || (a2 && b0)) && ((!a0 && b1) || (a0 && b2))) c1 = 1;
if (((a1 && b0) || (a2 && !b0)) && ((a0 && b1) || (!a0 && b2))) c2 = 1;
return c0 + c1 * 2 + c2 * 4;
}
int op[8][8];
void cal(ll a[], ll b[]) {
clr();
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
fu[op[i][j]] = (fu[op[i][j]] + a[i] * b[j]) % MOD;
for (int i = 0; i < 8; i++) a[i] = (fu[i] + a[i] + b[i]) % MOD;
}
void pushup(ll a[]) {
clr();
for (int i = 0; i < 8; i++) {
if (a[i] == 0) continue;
int a0 = i & 1, a1 = i & 2;
int p = 3 - a0;
if (a1) fu[p] = (fu[p] + a[i]) % MOD;
else fu[p] = (fu[p] + a[i] * 2) % MOD;
}
for (int i = 0; i < 8; i++) a[i] = fu[i];
a[3] = (a[3] + 1) % MOD;
}
void dfs(int u) {
//sort(V[u].begin(), V[u].end());
for (int v : V[u]) {
dfs(v);
cal(dp[u], dp[v]);
}
pushup(dp[u]);
}
int main() {
//freopen("0.txt", "r", stdin);
scanf("%d", &n);
for (int i = 2, x; i <= n; i++) scanf("%d", &x), V[x].push_back(i);
for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) op[i][j] = f(i, j);
dfs(1);
ll ans = 0;
for (int i = 0; i < 8; i++) ans += dp[1][i];
printf("%lld\n", ans % MOD);
return 0;
}