1. 程式人生 > 實用技巧 >JZOJ 4276【NOIP2015模擬10.28A組】遞推

JZOJ 4276【NOIP2015模擬10.28A組】遞推

【NOIP2015模擬10.28A組】遞推

思路一

對於 \(30%\) 的資料,由於 \(n\)\(x_i\) 都比較小,所以依題暴力列舉每個整點的座標算貢獻即可

思路二

對於額外 \(20%\) 的資料,發現 \(n=1\) 且有數列 \(F\) 為斐波那契數列,於是就變成求 \(\sum_{i=0}^{x_0 - 1}Fib_i\)
於是我們可以矩陣優化求和

思路三

既然提到矩陣,我們不妨順著這個思路來想
如果只有一維,我們很容易用矩陣加速遞推切掉它

那麼考慮高維

發現唯一剩下的問題是如何計算括號中的矩陣之和
因為他們出現了等比
於是考慮暫且拋開單位矩陣 \(I\)
記 F = \(A + A^2 + A^3 + A^4 + ... + A^n\)


\(mid = \lfloor \frac{n}{2} \rfloor\)

\[F(n) = \left \{ \begin{aligned} (A + A^2 + A^3 + A^4 + ... + A^{mid})(A^{mid} + I) & & (\texttt{n is even}) \\ (A + A^2 + A^3 + A^4 + ... + A^{mid})(A^{mid} + I) + A^n & & (\texttt{n is odd}) \end{aligned} \right. \]

由於題目比較噁心,即使 \(A^n\) 用矩陣快速冪算也會 \(T\)


因為快速冪和分治過程性質一樣
所以我們考慮在分治的過程中算出 \(A^n\)
詳見程式碼

\(Code\)

#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;

const int N = 15 , M = 45;
const int P = 1e9 + 9;
int n , m , c[M] , f[M];
LL ans;

struct matrix{
	int m[M][M];
}A , Q , I , Now , Sum , res;

inline matrix Mul(matrix a , matrix b) //矩陣乘法
{
	memset(res.m , 0 , sizeof res.m);
	for(register int i = 1; i <= m; i++)
		for(register int j = 1; j <= m; j++)	
			for(register int k = 1; k <= m; k++)
				res.m[i][j] = (res.m[i][j] + 1LL * a.m[i][k] * b.m[k][j] % P) % P;
	return res;
}

inline matrix Plus(matrix a , matrix b) //矩陣加法
{
	memset(res.m , 0 , sizeof res.m);
	for(register int i = 1; i <= m; i++)
		for(register int j = 1; j <= m; j++)
			res.m[i][j] = (a.m[i][j] + b.m[i][j]) % P;
	return res;
}

inline matrix divide(int x)
{
	if (x == 1) return Q = A;
	int mid = x >> 1;
	matrix tmp = divide(mid); //分而治之
	matrix temp;
	temp = Mul(tmp , Q); 
	tmp = Plus(tmp , temp);
	Q = Mul(Q , Q); //平方算A^{2*mid}
	if (!(x & 1)) return tmp;
	Q = Mul(Q , A); //奇數時再乘個A,和快速冪同理
	tmp = Plus(tmp , Q); 
	return tmp;
}

int main()
{
	freopen("recursion.in" , "r" , stdin);
	freopen("recursion.out" , "w" , stdout);
	scanf("%d%d" , &n , &m);
	for(register int i = 1; i <= m; i++) scanf("%d" , &c[i]);
	for(register int i = 0; i < m; i++) scanf("%d" , &f[i]);
	for(register int i = 1; i <= m; i++) A.m[i + 1][i] = 1 , I.m[i][i] = Sum.m[i][i] = 1;
	for(register int i = 1; i <= m; i++) A.m[i][m] = c[m - i + 1];
	
	int x;
	for(register int i = 1; i <= n; i++)
	{
		scanf("%d" , &x);
		Q = I;
		Now = divide(x - 1);
		for(register int j = 1; j <= m; j++) Now.m[j][j] = (Now.m[j][j] + 1) % P;  //加上單位矩陣I
		Sum = Mul(Now , Sum); //先算括號中的
	}
	for(register int i = 1; i <= m; i++) //把f乘上來
		ans = (ans + 1LL * f[i - 1] * Sum.m[i][1] % P) % P;
	printf("%lld" , ans);
}