2019校赛E题

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;
} 

猜你喜欢

转载自blog.csdn.net/Sarah_Wang0220/article/details/89674392