题目描述
给你n个flag,你要把每个染色成红黑白黄四色之一,满足:
1.相邻旗不能同色
2.白不能和黄相邻,红不能和黑相邻
3.不能存在连续三个球依次是“黑白红”或“红白黑”
4.翻转后相等视为等价
设不等价方案数为f(n),给定l,r,求
Sigma f(i),其中L<=i<=R模1000000007
输入
输入两个数l,r
l, r ≤ 10^9
输出
输出答案
样例输入
3 4
样例输出
23
题解
矩阵乘法
容易设出dp状态 $f[i][j][k]$ 表示前 $i$ 个flag,最后一个的颜色为 $j$ ,倒数第二个的颜色为 $k$ 的方案数。
显然这个dp方程可以使用矩阵乘法来加速转移,并使用计数器维护前缀和。
至于翻转后相等视为等价的问题,易知:答案=(总方案数+翻转后与原来相等的方案数)/2。于是求出反转后与原来相等的方案数即可。
容易发现偶数长度的中间两个一定相同,因此不存在偶数长度的回文串。
对于奇数长度,发现题目条件的限制是对称的(AB<=>BA,ABC<=>CBA),因此某长度为 $2k-1$ 的奇数长度回文串的个数即为长度为 $k$ 的串的个数。再次求 $lceilfrac n2 ceil$ 的答案即可。
最后前缀相减即为最终答案。
时间复杂度 $O(9^3log n)$
#include <cstdio> #include <cstring> #include <algorithm> #define mod 1000000007 using namespace std; typedef long long ll; struct data { ll v[9][9]; data() {memset(v , 0 , sizeof(v));} ll *operator[](int a) {return v[a];} data operator*(data &a) { data ans; int i , j , k; for(i = 0 ; i <= 8 ; i ++ ) for(j = 0 ; j <= 8 ; j ++ ) for(k = 0 ; k <= 8 ; k ++ ) ans[i][j] = (ans[i][j] + v[i][k] * a[k][j]) % mod; return ans; } }A; data pow(data x , int y) { data ans; int i; for(i = 0 ; i <= 8 ; i ++ ) ans[i][i] = 1; while(y) { if(y & 1) ans = ans * x; x = x * x , y >>= 1; } return ans; } void init() { int i; for(i = 0 ; i <= 8 ; i ++ ) A[i][0] = 1; A[1][5] = A[1][7] = 1; A[2][5] = A[2][7] = 1; A[3][6] = A[3][8] = 1; A[4][6] = A[4][8] = 1; A[5][1] = A[5][3] = 1; A[6][1] = A[6][3] = 1; A[7][2] = 1; A[8][4] = 1; } ll calc(int x) { if(!x) return 0; data T = pow(A , x - 1); ll ans = T[0][0] * 4; int i; for(i = 1 ; i <= 8 ; i ++ ) ans += T[i][0]; return ans % mod; } int main() { int l , r; scanf("%d%d" , &l , &r) , l -- ; init(); printf("%lld " , ((calc(r) + calc((r + 1) / 2) - calc(l) - calc((l + 1) / 2)) * 500000004 % mod + mod) % mod); return 0; }