gym 101889 :G.Gates of uncertainty(树形dp)

题目大意:有一堆逻辑门,它的逻辑满足下图
在这里插入图片描述
一堆逻辑门构成的一个逻辑图是一个树形图(显然),例如:
在这里插入图片描述
现在有些逻辑门出现了故障,有些逻辑门固定输出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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值