51nod1764 随机着色

题面

在这里插入图片描述
题目链接

解题思路

我们可以通过三组特征值来代表一颗子树,从而消除前效性。
第一组是子树大小的奇偶性。
第二组是子树左右翻转后是否相同,即从最左端开始染色和从最右端开始染色是否相同。
第三组是子树从最左边开始染白色和从最右边开始染黑色是否相同。
依据这三组特征值,我们可以代表一颗子树,从而进行转移。
复杂度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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值