原题: https://vjudge.net/problem/Gym-101889G
题意:
有多个与非门连接而成的二叉树形电路,现在有几个门坏了,即无论输入什么都输出1或0,你已经知道了坏的具体情况,问有多少种输入方式使门一的输出错误(即如果所有门都正常时的输出和现在的输出不符)
解析:
第一眼就看出来是个dp,但是怎么构造想了一会。
dp[i][f][out] 表示第i个门正确性为f时输出为out的方案数,例如:dp[3][1][0] 表示第3个门正确的输出0的方案数
答案数为dp[1][0][0]+dp[1][0][1]
那么其实转换就比较方便了,先用与非算出如果没有出错时的输出,dp[i][1][x]的原来为x,dp[i][0][x]的原来为!x;再用现在的状况计算出输出,dp[i][f][0]为0,dp[i][f][1]为1
如果两个输出相同就归结到dp[i][1][x],否则dp[i][0][x]
#include<bits/stdc++.h>
using namespace std;
#define debug(x) printf("# %d\n",x)
#define pill pair<int,int>
#define LL long long
int read(){int a;scanf("%d",&a);return a;}
const int N=1e5+5;
const LL mod=1e9+7;
int f[N];
LL dp[N][2][2];
int son[N][2];
int door(int a,int b){if(a==1&&b==1)return 0;return 1;}
void dfs(int p){
int l=son[p][0],r=son[p][1];
if(l)dfs(l);if(r)dfs(r);
for(int i=0;i<2;i++){//正确
for(int j=0;j<2;j++){//输入
LL _1=dp[l][i][j];
if(!_1)continue;
for(int k=0;k<2;k++){
for(int h=0;h<2;h++){
LL _2=dp[r][k][h];
if(!_2)continue;
int a=j,b=h;
if(!i)a=!a;if(!k)b=!b;
int outpre=door(a,b),outnow=door(j,h);
if(f[p]!=-1)outnow=f[p];
if(outnow==outpre){
dp[p][1][outnow]=(dp[p][1][outnow]+_1*_2%mod)%mod;
}
else{
dp[p][0][outnow]=(dp[p][0][outnow]+_1*_2%mod)%mod;
}
}
}
}
}
}
int main(){
int n=read();
for(int i=1;i<=n;i++){
son[i][0]=read(); son[i][1]=read(); f[i]=read();
}
dp[0][1][1]=dp[0][1][0]=1;
dfs(1);
LL ans=(dp[1][0][1]+dp[1][0][0])%mod;
printf("%lld\n",ans);
}