码迷,mamicode.com
首页 > 其他好文 > 详细

【U21729】递推数列

时间:2018-03-19 13:24:35      阅读:150      评论:0      收藏:0      [点我收藏+]

标签:post   乘法   git   int   amp   需要   pac   转移   under   

已知数列 $f$ 满足

$$ f(0) = 0, f(1) = 1, f(n) = f(n-1) + 3f(n-2) $$

有 $t = {10} ^ 5$ 组询问,每次给定非负整数 $n \le {10} ^ {18}$ ,请你求出

$$
\sum_{i = 0} ^ {n-1} i f(i) \mod 10009
$$

 

做法 1 :

$$\begin{pmatrix} & & 1 & & \\ & & & 1 & \\ 3 & & 1 & & \\ 6 & 3 & 1 & 1 & \\ & & & 1 & 1 \end{pmatrix} \begin{pmatrix} f_{n-2} \\ (n-2)f_{n-2} \\ f_{n-1} \\ (n-1)f_{n-1} \\ \sum_{i \le {n - 2} i f_i} \end{pmatrix} = \begin{pmatrix} f_{n-1} \\ (n-1)f_{n-1} \\ f_{n} \\ nf_{n} \\ \sum_{i \le {n-1} i f_i} \end{pmatrix}$$

复杂度为 $O(t \log n)$ ,带有 $125$ 倍的常数。

如果不使用 Caley-Hamition 定理,那么能不能过,就依赖于你的常数了。

 

矩阵乘法可以处理系数。

如果要处理 $\sum i ^ k f(i)$ ,考虑维护所有 $j \le k, \sum i ^ j f(i)$ ,根据二项式定理即可转移。

如果要处理 $\sum i ^ {\underline{k}} f(i)$ ,考虑描述成组合数的形式,根据二项式的加法公式进行转移。

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define F(i, s, t) for (int i = (s), _ = (t); i <= _; i ++)
 4 #define Fo(i, s, t) for (int i = (s), _ = (t); i < _; i ++)
 5 #define LL long long
 6 const int P = 10009;
 7 struct Mat {
 8     int h, w, a[5][5];
 9     Mat(int _1 = 0, int _2 = 0) : h(_1), w(_2) { memset(a, 0, sizeof a); }
10     int *operator [] (int x) { return a[x]; }
11     Mat operator * (Mat &B) {
12         Mat C(h, B.w);
13         Fo(k, 0, w)
14             Fo(i, 0, C.h) if (a[i][k])
15                 Fo(j, 0, C.w)
16                     C.a[i][j] = (C.a[i][j] + (LL)a[i][k] * B.a[k][j]) % P;
17         return C;
18     } 
19 }E, B, C, f;
20 namespace Input {
21     const int S = 10000000;
22     char s[S], *h = s+S, *t = h;    
23     char nchar() {
24         if (h == t) fread(s, 1, S, stdin), h = s;
25         return *h ++;
26     }
27     LL rd() {
28         LL x = 0, f = 1; char c;
29         do { c = nchar(); if (c == -) f = -1; } while (!isdigit(c));
30         do { x = x*10+c-0, c = nchar(); } while (isdigit(c));
31         return x * f;
32     }
33 }
34 using Input::rd;
35 Mat Pow(Mat A, LL n) {
36     if (! n) return E;
37     Mat t = Pow(A, n >> 1);
38     t = t * t;
39     if (n & 1) t = t * B;
40     return t;
41 }
42 int main() {
43     #ifndef ONLINE_JUDGE
44         freopen("729.in", "r", stdin);
45     #endif
46     E = Mat(5, 5), E[0][0] = E[1][1] = E[2][2] = E[3][3] = E[4][4] = 1;
47     B = Mat(5, 5), B[0][2] = B[1][3] = B[2][2] = B[3][2] = B[3][3] = B[4][3] = B[4][4] = 1, B[2][0] = B[3][1] = 3, B[3][0] = 6;
48     for (int cas = rd(); cas --; ) {
49         LL n = rd();
50         if (! n) { puts("0"); continue; }
51         C = Pow(B, n-1);
52         f = Mat(5, 1), f[0][0] = f[1][0] = f[4][0] = 0, f[2][0] = f[3][0] = 1;
53         f = C * f;
54         printf("%d\n", f[4][0]);
55     }
56     return 0;
57 }

 

做法 2 :

利用特征方程,解出 $f_n = \frac{1}{\sqrt {13}} (\frac{1 + \sqrt{13}}{2}) ^ n - \frac{1}{\sqrt{13}} (\frac{1 - \sqrt{13}}{2}) ^ n$ ,恰好 $13$ 在模 $10009$ 下有平方根,所以现在的问题变为求:

$$F(n) = \sum_{i = 0} ^ {n - 1} i q ^ i$$

一种做法是利用国王奇遇记的方法,一定存在一次多项式 $g(n)$ ,使得 $F(n) = q ^ n g(n) - g(0)$ ,求出 $g$ 的前两项然后插值,或者求出系数然后直接代入。

另外一种做法是求出 $i q ^ i$ 的前缀和(也就是逆差分函数),求的时候需要使用分部求和的技巧,

$$\Delta (uv) = Eu Ev - uv = Eu \Delta v + \Delta u v$$
$$uv = \Sigma Eu \Delta v + \Sigma \Delta uv$$
$$\Sigma (\Delta u) v = uv - \Sigma (Eu) (\Delta v)$$
$$\Sigma i q ^ i = \frac{q ^ i i}{q-1} - \Sigma \frac{q ^ {i + 1}}{q - 1}$$
$$\sum_{i = 0} ^ {n - 1} i q ^ i = \frac{q ^ n n}{q - 1} - \frac{q ^ {n + 1}}{(q - 1) ^ 2} + \frac{q}{(q - 1) ^ 2}$$

代入 $q = 2$ 进行检验是没错的,所以应该能够信任这个结果。 

 

 1 // f(n) = 4683 * 413 ^ n + -4683 * -412 ^ n
 2 #include <bits/stdc++.h>
 3 using namespace std;
 4 #define LL long long
 5 const int P = 10009;
 6 namespace Input {
 7     const int S = 10000000;
 8     char s[S], *h = s+S, *t = h;
 9     char nchar() {
10         if (h == t) fread(s, 1, S, stdin), h = s;
11         return *h ++;
12     }
13     LL rd() {
14         LL x = 0, f = 1; char c;
15         do { c = nchar(); if (c == -) f = -1; } while (!isdigit(c));
16         do { x = x*10+c-0, c = nchar(); } while (isdigit(c));
17         return x * f;
18     }
19 }
20 using Input::rd;
21 int Pow(int x, LL t) {
22     int m = 1;
23     for (; t; t >>= 1, x = (LL)x * x % P)
24         if (t & 1)
25             m = (LL)m * x % P;
26     return m;
27 }
28 int Inv(int x) { return Pow(x, P-2); }
29 int C(LL n, int q) {
30     int I = Inv(q-1);
31     int u = Pow(q, n);
32     return ((LL)u * (n % P) * I - (LL)u * q * I * I + (LL)q * I * I) % P;
33 }
34 int T(LL n) {
35     int s = C(n, 413);
36     s = 4683 * (s - C(n, -412)) % P;
37     return s += (s < 0 ? P : 0);
38 }
39 int main() {
40     #ifndef ONLINE_JUDGE
41         freopen("729.in", "r", stdin);
42     #endif
43     for (int cas = rd(); cas --; ) {
44         LL n = rd();
45         printf("%d\n", T(n));
46     }
47     return 0;
48 }

 

【U21729】递推数列

标签:post   乘法   git   int   amp   需要   pac   转移   under   

原文地址:https://www.cnblogs.com/Sdchr/p/8600947.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!