题目大意:有一堆逻辑门,它的逻辑满足下图
一堆逻辑门构成的一个逻辑图是一个树形图(显然),例如:
现在有些逻辑门出现了故障,有些逻辑门固定输出1,有些逻辑门固定输出0。求有多少种输入对应的输出是不正确的?
这个题显然可以用树形DP做,先考虑最后答案怎么得到:用根节点原本要输出 0,但实际上输出1的输入数量(下面称它为错误输出 1)加上 原本要输出1实际输出0的输入数量(称它为错误输出 0)。
令 dp[u][0] 表示 u 点错误输出 0 的输入个数,dp[u][1] 表示 u 点错误输出 1 的个数。考虑维护和转移 dp[u][i] ,就是对着逻辑表分类讨论合并一下,会发现维护 dp[u][i] 需要用到 另外一个 变量:得到正确输出的输入个数,令 tp[u][i] 表示 u 点正确输出 1 的输入个数,tp[i][0] 表示 u 点正确输入 0 的个数。
那么再考虑 tp[u][i] 怎么维护,也是根据逻辑表分类讨论,会发现会用到 dp数组,并且是可维护的,那么这题就做完了。
对于坏掉的门,把一些 tp,dp转移过去,并请空即可,具体看代码吧
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
ll dp[maxn][2],tp[maxn][2];
int v[maxn],n;
vector<int> g[maxn];
void dfs(int u,int fa) {
if(!u) return;
int ls = g[u][0],rs = g[u][1];
dfs(ls,u);dfs(rs,u);
dp[u][1] = (dp[ls][0] * dp[rs][0] % mod + dp[ls][0] * tp[rs][1] % mod + tp[ls][1] * dp[rs][0] % mod) % mod;
dp[u][0] = (dp[ls][1] * dp[rs][1] % mod + dp[ls][1] * tp[rs][1] % mod + tp[ls][1] * dp[rs][1] % mod) % mod;
tp[u][0] = (tp[ls][1] * tp[rs][1] % mod);
tp[u][1] = (dp[ls][1] * tp[rs][0] % mod + tp[ls][0] * dp[rs][1] % mod + dp[ls][1] * dp[rs][0] % mod
+ dp[ls][0] * dp[rs][1] % mod + dp[ls][0] * tp[rs][0] % mod + tp[ls][0] * dp[rs][0] % mod) % mod;
tp[u][1] += (tp[ls][0] * tp[rs][1] % mod + tp[ls][0] * tp[rs][0] % mod + tp[ls][1] * tp[rs][0] % mod) % mod;
tp[u][1] %= mod;
if(v[u] == 0) {
dp[u][0] = (dp[u][0] + tp[u][1]) % mod;
tp[u][0] = (dp[u][1] + tp[u][0]) % mod;
tp[u][1] = dp[u][1] = 0;
} else if(v[u] == 1) {
dp[u][1] = (dp[u][1] + tp[u][0]) % mod;
tp[u][1] = (tp[u][1] + dp[u][0]) % mod;
dp[u][0] = tp[u][0] = 0;
}
}
int main() {
scanf("%d",&n);
dp[0][0] = dp[0][1] = 0;
tp[0][0] = tp[0][1] = 1;
for(int i = 1; i <= n; i++) {
int l,r;
scanf("%d%d%d",&l,&r,&v[i]);
g[i].push_back(l);
g[i].push_back(r);
}
dfs(1,0);
printf("%lld\n",(dp[1][0] + dp[1][1]) % mod);
return 0;
}