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 }