容易想到定义 d p [ i ] [ j ] dp[i][j] dp[i][j]为处理完第 i i i个运算符时,答案为 j j j的概率
那么初始化 d p [ 0 ] [ a [ 0 ] ] = 1.0 dp[0][a[0]]=1.0 dp[0][a[0]]=1.0
转移也非常好写
dp[0][a[0]]=1.0;
for(int i=1;i<=n;i++)
for(int j=0;j<=maxx;j++)
{
dp[i][j]+=dp[i-1][j]*p[i];
if( s[i]=='&' )
dp[i][j&a[i]]+=dp[i-1][j]*(1.0-p[i]);
else if( s[i]=='^' )
dp[i][j^a[i]]+=dp[i-1][j]*(1.0-p[i]);
else
dp[i][j|a[i]]+=dp[i-1][j]*(1.0-p[i]);
}
double ans=0;
for(int i=0;i<=maxx;i++) ans += dp[n][i]*i;
这样 a n s ans ans就是最终答案,算出来也能对上样例
但是,时间,空间复杂度已经上天了
于是转换思维,每次只去 d p dp dp每一位二进制是 1 1 1的期望
令 l i m lim lim为当前二进制 x x x位为 1 1 1的概率
分情况讨论即可,分为符号消失或者不消失
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int t,n,a[1<<21];
char s[209];
double p[maxn];
int main()
{
int casenum=0;
while( cin >> n )
{
for(int i=0;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++) cin >> s[i];
for(int i=1;i<=n;i++) cin >> p[i];
double ans=0;
for(int i=0;i<=20;i++)
{
int u=(1<<i); double lim=(a[0]&u)?1.0:0;
for(int j=1;j<=n;j++)
{
double temp=lim;
if( s[j]=='&' )
{
if( !(a[j]&u) ) temp=0;
}
else if( s[j]=='|' )
{
if( a[j]&u ) temp=1.0;
}
else
{
if( a[j]&u ) temp=1.0-temp;
}
lim=lim*p[j]+temp*(1.0-p[j]);
}
ans+=lim*u;
}
printf("Case %d:\n",++casenum);
printf("%.6lf\n",ans);
}
}