HDU4649

容易想到定义 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);
	}
}