E.Saiki with Fairy Algorithm (neuoj1485)
【题目】
题目链接
S-运算法则:有n个数字a_i,n-1个运算符(只包含±*),每次选择任意相邻数字运算,最终只剩下一个数字,只要有一次操作不同,则视为不同的操作,求不同操作所得结果的和,mod1000000007
【思路】
最近在看组合数学,看到栈就想到卡特兰数,果然是越学越傻了
这题n的范围只有100,但如果暴力的话100!果断TLE
学长提示是DP,dp[i][j]表示数字串i的确n<=100的范围+字符串操作很容易联想到区间DP,不过这题还有一个考点是组合数
其实不同的操作就是运算符的全排列,举个栗子,对操作符串而言,当处理[1],[3,4](2是被选中的操作符)时,前者的全排列为1,后者的全排列为{4,3},{3,4},由于2是最终要进行的操作,所以两边的操作互不干涉,可以不改变相对位置的将左右的全排列混合得到新的排列,如对于{1}{4,3}来说{1,4,3}{4,1,3}{4,3,1}的结果是一样的,由重排列计算公式可得(j-i-1)!/[(t-i)(j-t-1)]=C(j-i-1,t-i) (这些下标是针对数字串而非操作串的)
再看,当被选中的操作是*时,根据乘法原理直接相乘即可;当被选中的操作时±时,左边结果要和右边的每一个排列结果相加,故乘上 len2!(右边的操作数目),同理,右边的结果要和左边的每一个排列结果相加,故乘上len1!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll P=1000000007;
const int MAX=100;
int n;
ll a[MAX+5],dp[MAX+5][MAX+5],f[MAX+5][MAX+5],fv[MAX+5];
char op[MAX+5];
void init(){
for(int i=0;i<=MAX;i++){
for(int j=0;j<=MAX;j++){
dp[i][j]=0;
}
}
memset(a,0,sizeof(a));
}
int amin(int a,int b){
return a<b?a:b;
}
int main(){
freopen("E.in","r",stdin);
freopen("E.out","w",stdout);
int i,j,k,t,x;
f[0][0]=1;
f[1][1]=1;
f[1][0]=1;
for(i=2;i<=MAX;i++){
f[i][0]=f[i][i]=1;
for(j=1;j<i;j++)
f[i][j]=(f[i-1][j-1]+f[i-1][j])%P;
}
fv[0]=1;
fv[1]=1;
for(i=2;i<=MAX;i++)
fv[i]=fv[i-1]*i%P;
while(scanf("%d",&n)!=EOF){
init();
for(i=1;i<=n;i++){
scanf("%I64d",&a[i]);
dp[i][i]=a[i];
}
scanf("%s",op);
for(k=1;k<n;k++){
for(i=1;i+k<=n;i++){
j=i+k;
for(t=i;t<j;t++){
if(op[t-1]=='*')
dp[i][j]+=(dp[i][t]*dp[t+1][j])%P*f[j-i-1][t-i]%P;
else{
if(op[t-1]=='+')
dp[i][j]+=(dp[i][t]*fv[j-t-1]%P+dp[t+1][j]*fv[t-i]%P)%P*f[j-i-1][t-i]%P;
else
dp[i][j]+=(dp[i][t]*fv[j-t-1]%P-dp[t+1][j]*fv[t-i]%P)%P*f[j-i-1][t-i]%P;
}
dp[i][j]%=P;
}
}
}
printf("%I64d\n",dp[1][n]);
}
return 0;
}