【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; }
| 欢迎来原网站坐坐! >原文链接<