2018ICPC焦作- Poor God Water 求递推式+矩阵快速幂
题目链接:https://nanti.jisuanke.com/t/31721
题意:一个孩子吃饭,有meat, fish 和 chocolate 三种食物可以选。要求连续三顿饭食物不能完全相同,鱼和肉的前一顿和后一顿不能都是巧克力,巧克力的左右两边不能同时出现鱼和肉。
思路:分九种情况,求出递推式,写出标准矩阵,用矩阵快速幂。
代码:
#include <iostream> #include <stdio.h> #include <string.h> #define maxn 10000000009 #define MOD 1000000007 #define ll long long using namespace std; ll jz[10][10] = { {0, 0,0,0,0,0,0,0,0,0}, {0, 0,0,0,1,0,0,1,0,0}, {0, 1,0,0,0,0,0,1,0,0}, {0, 1,0,0,1,0,0,1,0,0}, {0, 0,1,0,0,1,0,0,0,0}, {0, 0,1,0,0,0,0,0,1,0}, {0, 0,0,0,0,1,0,0,1,0}, {0, 0,0,1,0,0,1,0,0,1}, {0, 0,0,1,0,0,0,0,0,1}, {0, 0,0,1,0,0,1,0,0,0} }; struct juzhen { ll a[10][10]; }arr[35]; ll n, t; void mulmod(ll a, ll b, ll &c) { ll a1, a2, b1, b2; a1 = a >> 1; a2 = a - a1; b1 = b >> 1; b2 = b - b1; c += (a1*b1) % MOD ; c %= MOD; c += (a1*b2) % MOD; c %= MOD; c += (a2*b1) % MOD; c %= MOD; c += (a2*b2) % MOD; c %= MOD; } void jzxjz(juzhen x, juzhen y, juzhen &jieguo) { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { jieguo.a[i][j] = 0; for (int k = 1; k <= 9; k++) { mulmod(x.a[i][k], y.a[k][j], jieguo.a[i][j]); } } } } int findmax(int l,int r,ll n) { //不要递归,会超时 int mid; while (r-l>1) { mid = (l + r) >> 1; mid++; if (n >= 1ll << mid) { l = mid; } else r = mid - 1; } return l; } void muljz(ll n, juzhen &jzn) { for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { jzn.a[i][j] = !(j ^ i); } } int k; while (n>0) { k = findmax(0, 34, n); jzxjz(jzn, arr[k], jzn); n -= 1ll << k; } } void setarr() { //arr[k] 代表初始矩阵的2^k次幂 for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { arr[0].a[i][j] = jz[i][j]; } } for (int i = 1; i <= 34; i++) { jzxjz(arr[i - 1], arr[i - 1], arr[i]); } } int main() { setarr(); juzhen jzn1; ll ans; scanf("%lld", &t); while (t--) { scanf("%lld", &n); if (n == 2) { printf("%d\n", 9); continue; } if (n == 1) { printf("%d\n", 3); continue; } muljz(n - 2, jzn1); ans = 0; for (int i = 1; i <= 9; i++) { for (int j = 1; j <= 9; j++) { ans += jzn1.a[i][j]; ans %= MOD; } } printf("%lld\n", ans); } return 0; }