【BZOJ5082】弗拉格 矩阵乘法

【BZOJ5082】弗拉格

Description

“如果明天进了面试,我就去爆妹子的照”——有妹子的丁相允作为一个oier,自然不能立太多flag,让我们来看一道和flag有关的题目吧
给你n个flag,你要把每个染色成红黑白黄四色之一,满足:
1.相邻旗不能同色
2.白不能和黄相邻,红不能和黑相邻
3.不能存在连续三个球依次是“黑白红”或“红白黑”
4.翻转后相等视为等价
设不等价方案数为f(n),给定l,r,求Sigmaf(i),其中L<=i<=R模1000000007

Input

输入两个数l,r
l, r ≤ 10^9

Output

输出答案

Sample Input

3 4

Sample Output

23

题解:题中所给的条件不难令人想到矩乘。用v[a][b]表示最后一个颜色是a,倒数第二个颜色是b的方案数,然后根据前3个要求手动构建转移矩阵即可。

那第4个要求怎么办呢?我们直接将总方案数/2,但是一个回文串翻转过来和自身相等,不应该/2,所以我们还需加上回文串的个数。并且长度为偶数的回文串显然不满足条件1,所以我们只需要统计出所有长度为奇数的回文串,其数量=所有长度为(n+1)/2的串的个数。

 

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const ll P=1000000007;
struct M
{
	ll v[17][17];
	M() {memset(v,0,sizeof(v));}
	ll* operator [] (int a) {return v[a];}
	M operator * (const M &b) const
	{
		M c;
		int i,j,k;
		for(i=0;i<=16;i++)	for(j=0;j<=16;j++)	for(k=0;k<=16;k++)	c.v[i][j]=(c.v[i][j]+v[i][k]*b.v[k][j])%P;
		return c;
	}
}S1,S2,T;
void pm(ll y1,ll y2)
{
	while(y1||y2)
	{
		if(y1&1)	S1=S1*T;
		if(y2&1)	S2=S2*T;
		T=T*T,y1>>=1,y2>>=1;
	}
}
//0白1红2黑3黄

inline ll calc(ll n)
{
	if(!n)	return 0;
	if(n==1)	return 4;
	if(n==2)	return 8;
	int i,j,k;
	T=S1=S2=M();
	for(i=0;i<4;i++)	for(j=0;j<4;j++)	if(i!=j&&i+j!=3)
	{
		for(k=0;k<4;k++)	if(j!=k&&j+k!=3&&!(i==2&&j==0&&k==1)&&!(i==1&&j==0&&k==2))
			T[i*4+j][j*4+k]=1,T[i*4+j][16]++;
		S1[0][i*4+j]=S2[0][i*4+j]=1;
	}
	T[16][16]=1;
	S1[0][16]=S2[0][16]=8;
	pm(n-2,(n+1)/2-2);
	return ((S1[0][16]+S2[0][16]+P)%P*500000004+4)%P;
}
int main()
{
	ll l,r;
	scanf("%lld%lld",&l,&r);
	printf("%lld",(calc(r)-calc(l-1)+P)%P);
	return 0;
}

 

原文地址:https://www.cnblogs.com/CQzhangyu/p/7898228.html