• Nowcoder 练习赛 17 C 操作数 ( k次前缀和、矩阵快速幂打表找规律、组合数 )


    题目链接

    题意 : 

    给定长度为n的数组a,定义一次操作为:

    1. 算出长度为n的数组s,使得si= (a[1] + a[2] + ... + a[i]) mod 1,000,000,007;

    2. 执行a = s;

    现在问k次操作以后a长什么样。

    分析 :

    这种不断求前缀和的操作、可以考虑构造操作矩阵、最后矩阵快速幂求答案

    设 dp[k][i] 为第 k 次操作、第 i 个数的值

    则可以得到递推式

    dp[k][1] = dp[k-1][1]

    dp[k][2] = dp[k-1][2] + dp[k][1]

    dp[k][3] = dp[k-1][3] + dp[k][2]

    ...

    dp[k][n] = dp[k-1][n] + dp[k][n-1]

    然后你会发现这个东西可以用矩阵乘法来替换

    则可以构造一个下三角矩阵 ( 举 n = 4 例子 )

    1 0 0 0

    1 1 0 0

    1 1 1 0

    1 1 1 1

    记为 A

    则有

      dp[k-1][1]                               dp[k][1]

      dp[k-1][2]                               dp[k][2]

      dp[k-1][3]                               dp[k][3]

          ……                *  A   =           ……

    则做 k 次前缀和操作、就是乘 A^k

    可是这里 n 太大了、进行矩阵乘法的话复杂度过不去

    考虑打表找规律

    最后你可以发现 A^k 的矩阵和杨辉三角 ( 即组合数 ) 有蜜汁规律

    最后矩阵可以变成 

    C(k, k)

    C(k+1, k)      C(k, k)

    C(k+2, k)      C(k+1, k)      C(k, k)

    C(k+3, k)      C(k+2, k)      C(k+1, k)     C(k, k)

    ......

    根据组合数公式 C(n, m) = C(n, n-m)

    C(k, 0)

    C(k+1, 1)      C(k, 0)

    C(k+2, 2)      C(k+1, 1)      C(k, 0)

    C(k+3, 3)      C(k+2, 2)      C(k+1, 1)     C(k, 0)

    ......

    所以只要对于给定的 k 求解所有的 C(k, 0) 、C(k+1, 1) ..... C(k+n, n)

    就能快速构造出这个矩阵

    最后进行矩阵乘法就是答案

    注意特判 k == 0 的情况

    #include<bits/stdc++.h>
    #define LL long long
    #define ULL unsigned long long
    
    #define scl(i) scanf("%lld", &i)
    #define scll(i, j) scanf("%lld %lld", &i, &j)
    #define sclll(i, j, k) scanf("%lld %lld %lld", &i, &j, &k)
    #define scllll(i, j, k, l) scanf("%lld %lld %lld %lld", &i, &j, &k, &l)
    
    #define scs(i) scanf("%s", i)
    #define sci(i) scanf("%d", &i)
    #define scd(i) scanf("%lf", &i)
    #define scIl(i) scanf("%I64d", &i)
    #define scii(i, j) scanf("%d %d", &i, &j)
    #define scdd(i, j) scanf("%lf %lf", &i, &j)
    #define scIll(i, j) scanf("%I64d %I64d", &i, &j)
    #define sciii(i, j, k) scanf("%d %d %d", &i, &j, &k)
    #define scddd(i, j, k) scanf("%lf %lf %lf", &i, &j, &k)
    #define scIlll(i, j, k) scanf("%I64d %I64d %I64d", &i, &j, &k)
    #define sciiii(i, j, k, l) scanf("%d %d %d %d", &i, &j, &k, &l)
    #define scdddd(i, j, k, l) scanf("%lf %lf %lf %lf", &i, &j, &k, &l)
    #define scIllll(i, j, k, l) scanf("%I64d %I64d %I64d %I64d", &i, &j, &k, &l)
    
    #define lson l, m, rt<<1
    #define rson m+1, r, rt<<1|1
    #define lowbit(i) (i & (-i))
    #define mem(i, j) memset(i, j, sizeof(i))
    
    #define fir first
    #define sec second
    #define VI vector<int>
    #define ins(i) insert(i)
    #define pb(i) push_back(i)
    #define pii pair<int, int>
    #define VL vector<long long>
    #define mk(i, j) make_pair(i, j)
    #define all(i) i.begin(), i.end()
    #define pll pair<long long, long long>
    
    #define _TIME 0
    #define _INPUT 0
    #define _OUTPUT 0
    clock_t START, END;
    void __stTIME();
    void __enTIME();
    void __IOPUT();
    using namespace std;
    const int maxn = 2e3 + 5;
    const LL mod = 1e9 + 7;
    
    LL arr[maxn];
    LL A[maxn][maxn];
    LL Comb[maxn];
    LL inv[maxn];
    
    inline void inv_init()
    {
        inv[0] = inv[1] = 1;
        for(int i=2; i<maxn; i++)
            inv[i] = (LL)(mod - mod / i) * inv[mod % i] % mod;
    }
    
    int main(void){__stTIME();__IOPUT();
    
        inv_init();
    
        int n; sci(n);
        LL k; scl(k);
    
        if(k==0){
            for(int i=1; i<=n; i++) scl(arr[i]);
            for(int i=1; i<=n; i++) printf("%lld ", arr[i]);
            puts("");
            return 0;
        }
    
        k--;
    
        Comb[0] = 1LL;
        for(int i=1; i<=n; i++){
            Comb[i] = Comb[i-1]%mod;
            Comb[i] = ( Comb[i] * (k + i)%mod )%mod;
            Comb[i] = ( Comb[i] * inv[i]%mod )%mod;
        }
    
        for(int i=1; i<=n; i++) scl(arr[i]);
        for(int i=1; i<=n; i++){
            for(int j=1; j<=i; j++){
                A[i][j] = Comb[i-j];
            }
        }
    
    //    for(int i=1; i<=n; i++,puts(""))
    //        for(int j=1; j<=n; j++)
    //            printf("%lld ", A[i][j]);
    
        for(int i=1; i<=n; i++){
            LL ans = 0;
            for(int j=1; j<=n; j++)
                ans = ((ans + (A[i][j] * arr[j])%mod + mod)%mod)%mod;
            printf("%lld", ans%mod);
            if(i != n) putchar(' ');
        }puts("");
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    __enTIME();return 0;}
    
    
    void __stTIME()
    {
        #if _TIME
            START = clock();
        #endif
    }
    
    void __enTIME()
    {
        #if _TIME
            END = clock();
            cerr<<"execute time = "<<(double)(END-START)/CLOCKS_PER_SEC<<endl;
        #endif
    }
    
    void __IOPUT()
    {
        #if _INPUT
            freopen("in.txt", "r", stdin);
        #endif
        #if _OUTPUT
            freopen("out.txt", "w", stdout);
        #endif
    }
    View Code
  • 相关阅读:
    李宏毅机器学习课程笔记-6.1神经网络训练问题与解决方案
    PAT甲级1055The World's Richest
    PAT甲级1028List Sorting
    PAT甲级1025PAT Ranking
    PAT甲级1016Phone Bills
    五边形数
    组合计数
    [编程题] lc三数之和 (借助哈希表)
    [编程题] 基础:如何使用大顶堆和小顶堆找topN
    [编程题] lc [剑指 Offer 54二叉搜索树的第k大节点----或者是求第K小元素]
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/9560519.html
Copyright © 2020-2023  润新知