【bzoj5082】弗拉格 矩阵乘法
题目描述
给你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$ 的串的个数。再次求 $\lceil\frac n2\rceil$ 的答案即可。
最后前缀相减即为最终答案。
时间复杂度 $O(9^3\log 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\n" , ((calc(r) + calc((r + 1) / 2) - calc(l) - calc((l + 1) / 2)) * 500000004 % mod + mod) % mod);
return 0;
}
浙公网安备 33010602011771号