做这道题的时候,想到会不会是dp,然后发现dp可做,但是一直被自己坑到死。
枚举最后合并的那个位置,然后对于加减号的,分成的前后两个部分都有不同的组合方法,
(a1+a2........) + (b1,b2.............) 对于每个a,被加b的个数的阶乘次 ,对于每个b,被加a的个数的阶乘次
减法同理
乘法特殊一点
(a1+a2........) * (b1,b2.............) 乘法分配率,直接将两部分的总和相乘即可
想到这些还远远没有结束,因为最后两个区间合并的时候,还牵扯到一个顺序问题,一开始还好傻好天真直接乘了2,结果呵呵哒了。
如果共有N个符号,i左边x个,右边y个,那么因为枚举了i这个最后合并的位置,所以最后一个进行运算的符号是确定的,处理完左右的得出的数字分别被加过多少次之后,还要处理这个得出的总数因为合并的顺序不同被多加过多少次,就是C(N-1,x) 其中选出的这x个位置中保持i左边符号的相对顺序不变,因为之前变过顺序的那个前面已经处理过了。。自己理解这里一开始有点问题,总是混,特别怕dp计数,尤其跟排列组合混起来,感觉脑子差一截……
#pragma comment(linker,"/STACK:102400000,102400000") #include <cstdio> #include <cstring> #include <vector> #include <queue> #include <cmath> #include <string> #include <map> #include <utility> #include <algorithm> #include <iostream> #define mem(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f using namespace std; int dx[]= {-1,0,1,0};///n e s w int dy[]= {0,1,0,-1}; typedef long long ll; typedef pair<int,int> pii; const int maxd=200+5; ///============================== const int mod=1000000007; ll dp[maxd][maxd],a[maxd],fac[maxd],c[maxd][maxd]; int n; char str[maxd]; void init() { fac[0]=1; for(int i=1; i<maxd; i++) fac[i]=fac[i-1]*i%mod; mem(c,0); for(int i=0; i<maxd; i++) { c[i][0]=1; for(int j=1; j<=i; j++) (c[i][j]=c[i-1][j-1]+c[i-1][j])%=mod; } } ll dfs(int l,int r) { ll &res=dp[l][r]; if(res!=mod) return res; if(l==r) return res=a[l]; res=0; for(int i=l; i<r ; i++) { ll tmp; if(str[i]=='*') tmp=dfs(l,i)*dfs(i+1,r)%mod; else if(str[i]=='-') tmp=(dfs(l,i)*fac[r-i-1]-dfs(i+1,r)*fac[i-l]+mod)%mod; else if(str[i]=='+') tmp=(dfs(l,i)*fac[r-i-1]+dfs(i+1,r)*fac[i-l])%mod; res=(res+tmp*c[r-l-1][i-l]+mod)%mod; } return res; } int main() { int kase; freopen("in.txt","r",stdin); init(); while(scanf("%d",&n)!=EOF) { for(int i=0; i<n; i++) scanf("%lld",&a[i]); scanf("%s",str); for(int i=0; i<n; i++) for(int j=0; j<n; j++) dp[i][j]=mod; cout<<(dfs(0,n-1)+mod)%mod<<endl; } return 0; } /* 3 3 2 1 -+ 5 1 4 6 8 3 +*-* 5 1 1 1 1 1 **** */
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-11-03 22:26:21