zoukankan      html  css  js  c++  java
  • ACM学习历程—HDU5396 Expression(递推 && 计数)

    Problem Description

    Teacher Mai has n numbers a1,a2,⋯,an and n−1 operators("+", "-" or "*")op1,op2,⋯,opn−1 , which are arranged in the form a1 op1 a2 op2 a3 ⋯ an .

    He wants to erase numbers one by one. In i -th round, there are n+1−i numbers remained. He can erase two adjacent numbers and the operator between them, and then put a new number (derived from this one operation) in this position. After n−1 rounds, there is the only one number remained. The result of this sequence of operations is the last number remained.


    He wants to know the sum of results of all different sequences of operations. Two sequences of operations are considered different if and only if in one round he chooses different numbers.

    For example, a possible sequence of operations for "1+4∗6−8∗3 " is 1+4∗6−8∗3→1+4∗(−2)∗3→1+(−8)∗3→(−7)∗3→−21 .

     

    Input

    There are multiple test cases.

    For each test case, the first line contains one number n(2≤n≤100) .

    The second line contains n integers a1,a2,⋯,an(0≤ai≤109) .

    The third line contains a string with length n−1 consisting "+","-" and "*", which represents the operator sequence.

     

    Output

    For each test case print the answer modulo 109+7 .

     

    Sample Input

    3

    3 2 1

    -+

    5

    1 4 6 8 3

    +*-*

     

    Sample Output

    2

    999999689

    Hint

    Two numbers are considered different when they are in different positions. 

    题目大意是给一个表达式自己可以随意定义运算符的运算顺序,求所有不同运算顺序得到的答案的和。

    首先总共的种数有(n-1)!个。这个就是一个乘法原理。

    但是99!这么多跑一遍就跪了。

    于是考虑一种策略,记s[i][j]表示从第i个到第j个数这个序列能得到的不同运算结果的和。

    对于最后一个运算的运算符假设是k,那么k可以取i, i+1, ..., j

    所以s[i][j] = sum(s[i][k] op s[k+1][j])

    关键是这个op运算如何实现,显然这个运算不是纯粹的+,-,*

    然后我们再看。对于s[i][j]这个式子,当a[j]加入时,这个式子才有意义。

    也就是说,如果之前所有的s[x][y]y < j)都计算出来后,才能通过a[j]计算出所有的s[x][j]

    所以上述式子应该是这样s[i][j] = f(i, j) = sum(s[i][k] op f(k+1, j))

    其中f(i, j)表示生成s[i][j]的函数,相当于f是新的s。当然这样写只是为了在递推的时候搞清楚k的顺序。这样如果搞不清k的递推顺序,就能通过这个式子进行记忆化搜索。

    接下来就是考虑这个op操作了。

    假设记s[i][k]对应的解集为A(即A中所有元素和为s[i][k]),s[k+1][j]对应的解集为B

    对于op’*’的:

    那么s[i][k] op s[k+1][j]应该为sum(Ai*Bj) = sum(Ai*sum(Bi)) = sum(Ai)*sum(Bi)

    但是这样并没有考虑AB中运算符的顺序,虽然对于一个Ai,必定是通过ik的元素经过一定的运算顺序才能得到。但是AiBi的运算顺序是不相干的(也就是说可以先在Ai这里算一个’+’,再在Bi这里算一个’*’),所以这里就变成一个计数问题了。AiBi需要排在一起,但是Ai中的元素不计排序,Bi同样的。

    所以相当于在j-i-1个位置中取k-i个位置给Ai。所以结果需要乘上C(k-i, j-i-1)

    得到了s[i][k] ‘*’ s[k+1][j] = sum(Ai)*sum(Bi)*C(k-i, j-i-1) = s[i][k] * s[k+1][j] * C(k-i, j-i-1) 

    对于op’+’的:

    同样的考虑,

    那么s[i][k] op s[k+1][j]应该为sumAi+Bj) = sum(kB*Ai+sum(Bi)) = kBsum(Ai)+kAsum(Bi),(其中kAkB分别表示AB集合元素的个数)

    最后结果再乘上个C(k-i, j-i-1) 

    于是s[i][k] ‘+’ s[k+1][j] = (kBsum(Ai)+kAsum(Bi))*C(k-i, j-i-1) 

    kA = (k-i)!, kB = (j-k-1)!可以通过乘法原理得到。

    注意C[0][0]也不能漏掉。 

    代码:

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <string>
    #include <algorithm>
    #define LL long long
    #define MOD 1000000007
    
    using namespace std;
    
    const int maxN = 105;
    int n, a[maxN];
    char op[maxN];
    LL A[maxN], C[maxN][maxN], s[maxN][maxN];
    
    void init()
    {
        A[0] = 1;
        for (int i = 1; i < maxN; ++i)
            A[i] = (A[i-1]*i)%MOD;
    
        for (int i = 0; i < maxN; ++i)
            C[0][i] = C[i][i] = 1;
        for (int i = 2; i < maxN; ++i)
            for (int j = 1; j < i; ++j)
                C[j][i] = (C[j][i-1]+C[j-1][i-1])%MOD;
    }
    
    inline LL cal(int x, int y, int z, char p)
    {
        LL ans;
        if (p == '+')
            ans = (s[x][y]*A[z-y-1]%MOD+s[y+1][z]*A[y-x]%MOD)%MOD;
        else if (p == '-')
            ans = ((s[x][y]*A[z-y-1]%MOD-s[y+1][z]*A[y-x]%MOD)%MOD+MOD)%MOD;
        else
            ans = (s[x][y]*s[y+1][z])%MOD;
        return (ans*C[y-x][z-x-1])%MOD;
    }
    
    void input()
    {
        memset(s, 0, sizeof(s));
        for (int i = 0; i < n; ++i)
        {
            scanf("%d", &a[i]);
            s[i][i] = a[i];
        }
        scanf("%s", op);
    }
    
    void work()
    {
        for (int j = 1; j < n; ++j)
            for (int i = j-1; i >= 0; --i)
                for (int k = j-1; k >= i; --k)
                    s[i][j] = (s[i][j]+cal(i, k, j, op[k]))%MOD;
        printf("%lld
    ", s[0][n-1]);
    }
    
    int main()
    {
        //freopen("test.in", "r", stdin);
        init();
        while (scanf("%d", &n) != EOF)
        {
            input();
            work();
        }
        return 0;
    }
  • 相关阅读:
    查看数据库所有的表
    oracle JOB学习(一)---基础
    图片实时预览JSP加js
    Collections.sort()
    FileUtil.java
    设计模式:常见类的关系
    枚举
    相册
    jQuery----blur()方法
    上传文件详解
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/4742015.html
Copyright © 2011-2022 走看看