3 3 2 1 -+ 5 1 4 6 8 3 +*-*
2 999999689HintTwo numbers are considered different when they are in different positions.
一个表达式,加不同的括号得到不同的计算顺序,只要有一个计算顺序不同这两个表达式就是不同的。求所有表达式的和。
用dp[l][r]表示第l个数到第r个数组成的各种顺序的表达式和是多少,t[l][r]表示第l个数到第r个数有多少种不同的组合。dp[l][r]的计算方法是枚举最后一个被计算的位置i,设n1=dp[l][i],n2=dp[i+1][r],t1=t[l][i],t2=t[i+1][r]。那么对于加号,对于每个i要加上n1*t2+n2*t1,对于右边不同的组合,左边的数每次都要被加一次,同理左边不同的组合,右边的数每次也要被加一次。因此n1被加了t2次,n2被加了t1次。减法和加法一样。乘法是直接n1*n2。这还没完,注意就算是左边的顺序和右边的顺序的确定,假设左边有f1个符号,右边有f2个符号,也有C[f1+f2][f1]种排法,相当于在f1+f2个位置中选f1个,剩下的给f2,f1和f2中排列的相对顺序不改变,所以还要乘上C[f1+f2][f1]。同理对于每个i,t[l][r]要加上t1*t2*C[f1+f2][f1]。
#include<iostream> #include<cstdio> #include<string> #include<cstring> #include<vector> #include<cmath> #include<queue> #include<stack> #include<map> #include<set> #include<algorithm> #pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; typedef long long LL; const int MAXN=110; const int INF=0x3f3f3f3f; const LL MOD=1e9+7; int T,N; int a[MAXN]; LL dp[MAXN][MAXN],t[MAXN][MAXN],C[MAXN][MAXN]; char str[MAXN]; void getC(){ for(int i=0;i<MAXN;i++){ C[i][0]=C[i][i]=1; for(int j=1;j<i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD; } } LL dfs(int l,int r){ LL& ret=dp[l][r]; if(ret!=-1) return ret; if(l==r){ t[l][r]=1; return ret=a[l]; } ret=0; t[l][r]=0; for(int i=l;i<r;i++){ LL n1=dfs(l,i),n2=dfs(i+1,r),t1=t[l][i],t2=t[i+1][r]; LL n=0; if(str[i]=='+') n=(n1*t2%MOD+n2*t1%MOD)%MOD; else if(str[i]=='-') n=(n1*t2%MOD-n2*t1%MOD+MOD)%MOD; else if(str[i]=='*') n=(n1*n2%MOD)%MOD; int f1=i-l,f2=r-i-1; n=n*C[f1+f2][f1]%MOD; ret=(ret+n)%MOD; t[l][r]=(t[l][r]+(t1*t2%MOD*C[f1+f2][f1]))%MOD; } return ret; } int main(){ getC(); while(scanf("%d",&N)!=EOF){ for(int i=1;i<=N;i++) scanf("%d",&a[i]); scanf("%s",str+1); memset(dp,-1,sizeof(dp)); printf("%I64d\n",dfs(1,N)); } return 0; }