注释:能打表找规律的绝对不推导了,省时,深有体会!!!
问题 G: 强
题目描述
Lh:粉兔你教我一下抽屉原理吧
Clz:就是给你一个长度为 n 的序列,每个数只能取 0,1,2,那你连续取三个数必然有两个相等……
Lh:等等你梭啥,再说一遍
Clz:……emmm 当我没说
Marser:就是一个序列,对于每一个连续三元组都要满足其中至少有两个相等
现在粉兔问你:有多少个长度为 n 的序列满足粉兔的要求?请对 19260817 取模。
输入
一行一个正整数n(3≤n≤1018)。
输出
一行一个整数,含义如题。
样例输入
4
样例输出
51
提示
51 种序列如下:
[0,0,0,0],[0,0,0,1],[0,0,0,2],[0,0,1,0],[0,0,1,1],[0,0,2,0],[0,0,2,2],[0,1,0,0],[0,1,0,1],[0,1,1,0],[0,1,1,1],[0,1,1,2],[0,2,0,0],[0,2,0,2],[0,2,2,0],[0,2,2,1],[0,2,2,2],[1,0,0,0],[1,0,0,1],[1,0,0,2],[1,0,1,0],[1,0,1,1],[1,1,0,0],[1,1,0,1],[1,1,1,0],[1,1,1,1],[1,1,1,2],[1,1,2,1],[1,1,2,2],[1,2,1,1],[1,2,1,2],[1,2,2,0],[1,2,2,1],[1,2,2,2],[2,0,0,0],[2,0,0,1],[2,0,0,2],[2,0,2,0],[2,0,2,2],[2,1,1,0],[2,1,1,1],[2,1,1,2],[2,1,2,1],[2,1,2,2],[2,2,0,0],[2,2,0,2],[2,2,1,1],[2,2,1,2],[2,2,2,0],[2,2,2,1],[2,2,2,2]。
解决思路:直接暴力打表数学归纳法,然后矩阵快速幂加速。
上图!!!
结论: { f ( 3 ) = 21 f ( 4 ) = 51 f ( n ) = 2 f ( n − 1 ) + f ( n − 2 ) \small \begin{cases} \ f(3)=21\\ \ f(4)=51\\ \ f(n)=2f(n-1)+f(n-2)\\ \end{cases} ⎩⎨⎧ f(3)=21 f(4)=51 f(n)=2f(n−1)+f(n−2)
构造矩阵:
//优化
#pragma GCC optimize(2)
//C
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
//C++
#include<unordered_map>
#include<algorithm>
#include<iostream>
#include<istream>
#include<iomanip>
#include<climits>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#include<set>
//宏定义
#define N 1010
#define DoIdo main
//#define scanf scanf_s
#define it set<ll>::iterator
//定义+命名空间
typedef long long ll;
typedef unsigned long long ull;
const ll mod = 19260817;
const ll INF = 1e18;
const int maxn = 1e6 + 10;
using namespace std;
//全局变量
const int m = 2;
struct mat {
ll m[3][3];
}ans;
//函数区
ll max(ll a, ll b) { return a > b ? a : b; }
ll min(ll a, ll b) { return a < b ? a : b; }
mat operator*(mat a, mat b) {
mat res;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= m; j++) {
ll temp = 0;
for (int k = 1; k <= m; k++) {
temp = (temp + a.m[i][k] * b.m[k][j]) % mod;
}
res.m[i][j] = temp;
}
}
return res;
}
void init() {
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= m; j++) {
if (i == j) ans.m[i][j] = 1;
else ans.m[i][j] = 0;
}
}
}
void quick_pow(mat a, ll b) {
init();
while (b) {
if (b & 1) {
ans = (ans * a);
}
a = (a * a);
b >>= 1;
}
}
//主函数
int DoIdo() {
ios::sync_with_stdio(false);
cin.tie(NULL), cout.tie(NULL);
ll n;
cin >> n;
mat A, B; A.m[1][1] = 21, A.m[1][2] = 51;
B.m[1][1] = 0, B.m[1][2] = 1;
B.m[2][1] = 1, B.m[2][2] = 2;
if (n >= 5) quick_pow(B, n - 4);
if (n == 3) cout << 21 << endl;
else if (n == 4) cout << 51 << endl;
else {
ll ans1 = ((A.m[1][1] * ans.m[1][2]) % mod + (A.m[1][2] * ans.m[2][2]) % mod) % mod;
cout << ans1 << endl;
}
return 0;
}
//分割线---------------------------------QWQ