Luogu U15118 萨塔尼亚的期末考试(fail)
感觉...昨天是真的傻...
题意
T个询问,每个询问给一个n,求
$ \frac{\sum_{n}^{i = 1}Fib_{i} * i}{n * (n + 1) / 2} $
Fib是斐波那契数列,对998244353取模
然后...我昨天把n乘了之后就忘记取模了...
30分做法
设$ a_{i} = i * Fib_{i} $, 有$ a_{i} = a_{i - 1} + a_{i - 2} + Fib_{i - 1} + Fib_{i - 2} $,然后就写了一个5*5的转移矩阵,感觉很稳……
长这样:
\begin{bmatrix}
Fib_{i} & Fib{i - 1} & a_{i} & a_{i - 1} & sum_{i - 1}
\end{bmatrix}
转移矩阵长这样:
\begin{bmatrix}
1 & 1 & 1 & 0 & 0 \\
1 & 0 & 2 & 0 & 0\\
0 & 0 & 1 & 1 & 1\\
0 & 0 & 1 & 0 & 0\\
0 & 0 & 0 & 0 & 1
\end{bmatrix}
虽然这个东西也过了几万组对拍,但是我喜闻乐见地把std也顺便写挂了(捂脸),所以这两个东西都只有10分...
Code:
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const ll mod = 998244353; int testCase; ll n; struct Matrix { ll l1, l2, s[5][5]; inline void init() { l1 = l2 = 0; memset(s, 0, sizeof(s)); } friend Matrix operator * (const Matrix a, const Matrix b) { Matrix res; res.init(); res.l1 = a.l1, res.l2 = b.l2; for(int i = 0; i < a.l1; i++) for(int j = 0; j < b.l2; j++) for(int k = 0; k < a.l2; k++) res.s[i][j] = (res.s[i][j] + a.s[i][k] * b.s[k][j] % mod) % mod; return res; } inline Matrix pow(Matrix a, ll b) { Matrix res = *this; for(; b > 0; b >>= 1) { if(b & 1) res = res * a; a = a * a; } return res; } inline void print() { for(int i = 0; i < l1; i++, printf("\n")) for(int j = 0; j < l2; j++) printf("%lld ", s[i][j]); } } f; inline ll pow(ll x, ll y) { ll res = 1; for(x %= mod; y > 0; y >>= 1) { if(y & 1) res = res * x % mod; x = x * x % mod; } return res; } inline ll solve() { if(n == 1) return 1LL; if(n == 2) return 3LL; Matrix g; g.init(); g.l1 = 1, g.l2 = 5; g.s[0][0] = g.s[0][1] = g.s[0][3] = 1; g.s[0][4] = 1; g.s[0][2] = 2; // g.print(); g = g.pow(f, n - 1); // g.print(); return g.s[0][4]; } int main() { f.init(); f.l1 = f.l2 = 5; f.s[0][0] = f.s[0][1] = f.s[0][2] = 1; f.s[1][0] = f.s[2][2] = f.s[2][3] = f.s[2][4] = 1; f.s[3][2] = f.s[4][4] = 1;//f.s[4][2] = 1; f.s[1][2] = 2; // f.print(); for(scanf("%d", &testCase); testCase--; ) { scanf("%lld", &n); ll tmp = n * (n + 1) / 2; ll inv = pow(tmp, mod - 2); ll sum = solve(); printf("%lld\n", inv * sum % mod); } return 0; }
100分做法
找规律题惹不起a...
$sum_{n} = n * Fib_{n + 2} - Fib_{n + 3} + 2$
所以就变成一个斐波那契了
并不能推导出来...
Code:
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const ll mod = 998244353; int testCase; ll n; struct Matrix { ll s[3][3]; inline void init() { memset(s, 0, sizeof(s)); } friend Matrix operator * (const Matrix a, const Matrix b) { Matrix res; res.init(); for(int i = 1; i <= 2; i++) for(int j = 1; j <= 2; j++) for(int k = 1; k <= 2; k++) res.s[i][j] = (res.s[i][j] + a.s[i][k] * b.s[k][j] % mod) % mod; return res; } inline Matrix pow(ll b) { Matrix res, a = *this; res.init(); for(int i = 1; i <= 2; i++) res.s[i][i] = 1; for(; b > 0; b >>= 1) { if(b & 1) res = res * a; a = a * a; } return res; } } f; inline ll pow(ll a, ll b) { ll res = 1; for(; b > 0; b >>= 1) { if(b & 1) res = res * a % mod; a = a * a % mod; } return res; } inline ll solve() { Matrix res = f.pow(n + 2); return (res.s[1][2] * n % mod - res.s[1][1] + 2 + mod) % mod; } int main() { f.init(); f.s[1][1] = f.s[1][2] = f.s[2][1] = 1; for(scanf("%d", &testCase); testCase--; ) { scanf("%lld", &n); ll inv = pow((n * (n + 1) / 2)% mod, mod - 2); ll sum = solve(); // printf("%lld %lld\n", sum, inv); printf("%lld\n", sum * inv % mod); } return 0; }
感觉还挺有趣的……