题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=5396
题意:
给出一个有n个数字,运算符只有"+","-","*"的表达式,每次合并相邻两项,求所有合并方式所得到的最终结果之和对1e9+7取模的值。
分析:
如果将这个过程随机化,即每次等概率地选取相邻两项合并,
记dp[i][j]为随机合并第i个到第j个数字这一段的表达式之后结果的期望,
根据期望的线性可加性,状态转移方程为
dp[i][j]=(sigma_(k=i~j-1)(dp[i][k]?dp[k+1][j]))/(j-i),
其中"?"表示第k个数与第k+1个数之间的运算符,
那么dp[1][n]即为随机合并整个表达式之后结果的期望,
乘上方案数(n-1)!即为所求的总和,
由于是取模意义下的运算,转移方程中的除法要用逆元代替,
复杂度O(n^3)。
代码:
#include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int MAXN=105; const ll Mod=1000000007LL; ll a[MAXN],f[MAXN]; char s[MAXN]; ll dp[MAXN][MAXN]; void build() { f[0]=1LL; for(int i=1;i<=100;i++) f[i]=i*f[i-1]%Mod; } ll fp(ll a,ll k) { ll res=1LL; while(k>0) { if(k&1)res=res*a%Mod; a=a*a%Mod; k>>=1; } return res; } int main() { build(); int n; while(scanf("%d",&n)!=EOF) { for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); scanf("%s",s+1); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][i]=a[i]; for(int len=1;len<n;len++) { for(int i=1;i+len<=n;i++) { int j=i+len; for(int k=i;k<j;k++) { if(s[k]=='+') dp[i][j]=(dp[i][j]+dp[i][k]+dp[k+1][j])%Mod; else if(s[k]=='-') dp[i][j]=(dp[i][j]+dp[i][k]-dp[k+1][j])%Mod; else dp[i][j]=(dp[i][j]+dp[i][k]*dp[k+1][j]%Mod)%Mod; } dp[i][j]=(dp[i][j]%Mod+Mod)%Mod; dp[i][j]=dp[i][j]*fp(len,Mod-2)%Mod; } } printf("%I64d\n",dp[1][n]*f[n-1]%Mod); } return 0; }