[HDU5396] Expression 题解

每次合并两个数,做过石子合并的人都能看出来是区间 dp。

设状态 \(dp_{i,j}\) 表示区间 \([i,j]\) 中合并为一个数的所有情况之和。

那么我们就可以枚举断点 \(k\)

  1. \(b_k\)\(+\)\([i,k]\) 中的每种情况都要和 \([k+1,j]\) 中的每种情况产生一个贡献,所以总贡献为 \(dp_{i,k}\times(j-k-1)!+dp_{k+1,j}\times(k-i)!\)
  2. \(b_k\)\(-\):同 1 理,贡献为 \(dp_{i,k}\times(j-k-1)!-dp_{k+1,j}\times(k-i)!\)
  3. \(b_k\)\(*\):同 1 理,贡献为 \(dp_{i,k}\times dp_{k+1,j}\)

由于可以穿插,每个贡献还要乘上 \(C_{j-i-1}^{k-i}\)

时间复杂度 \(O(n^2t)\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll p=1e9+7;
int n,a[105];
char b[105];
ll jc[105],inv[105];
ll dp[105][105];
ll qpow(ll x,int y){
    ll re=1;
    while(y){
        if(y&1) re=re*x%p;
        x=x*x%p;
        y>>=1;
    }return re;
}ll C(int x,int y){
    return jc[x]*inv[y]%p*inv[x-y]%p;
}void zjy_zkal(){
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++)
        cin>>dp[i][i];
    cin>>(b+1);
    for(int l=2;l<=n;l++)
        for(int i=1,j=l;j<=n;i++,j++)
            for(int k=i;k<j;k++){
                ll add=0;
                if(b[k]=='*')
                    add=dp[i][k]*dp[k+1][j]%p;
                if(b[k]=='+')
                    add=(dp[i][k]*jc[j-k-1]+dp[k+1][j]*jc[k-i])%p;
                if(b[k]=='-')
                    add=(dp[i][k]*jc[j-k-1]-dp[k+1][j]*jc[k-i])%p;
                dp[i][j]=(dp[i][j]+add*C(j-i-1,k-i))%p;
            }
    cout<<(dp[1][n]+p)%p<<"\n";
}int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    jc[0]=1;
    for(ll i=1;i<=100;i++)
        jc[i]=jc[i-1]*i%p;
    inv[100]=qpow(jc[100],p-2);
    for(ll i=100;i;i--)
        inv[i-1]=inv[i]*i%p;
    while(cin>>n) zjy_zkal();
    return 0;
}
posted @ 2024-03-23 11:28  长安一片月_22  阅读(3)  评论(0编辑  收藏  举报