bzoj5082弗拉格 矩阵乘法
Posted GXZlegend
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 <cctype> #include <cstring> #include <algorithm> #define N 1010 using namespace std; int n , m , a[N][N] , c[N][N] , v[N] , tot; bool solve(int mid) { int i , j , k; for(i = 1 ; i <= m ; i ++ ) for(j = i + 1 ; j <= m ; j ++ ) c[i][j] = 0; for(i = 1 ; i <= n ; i ++ ) { tot = 0; for(j = 1 ; j <= m ; j ++ ) if(a[i][j] >= mid) v[++tot] = j; for(j = 1 ; j <= tot ; j ++ ) { for(k = j + 1 ; k <= tot ; k ++ ) { if(c[v[j]][v[k]]) return 1; c[v[j]][v[k]] = 1; } } } return 0; } inline char nc() { static char buf[100000] , *p1 , *p2; return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ; } inline int read() { int ret = 0; char ch = nc(); while(!isdigit(ch)) ch = nc(); while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ ‘0‘) , ch = nc(); return ret; } int main() { int i , j , l = 1 << 30 , r = 0 , mid , ans; n = read() , m = read(); for(i = 1 ; i <= n ; i ++ ) for(j = 1 ; j <= m ; j ++ ) a[i][j] = read() , l = min(l , a[i][j]) , r = max(r , a[i][j]); while(l <= r) { mid = (l + r) >> 1; if(solve(mid)) ans = mid , l = mid + 1; else r = mid - 1; } printf("%d\n" , ans); return 0; }
以上是关于bzoj5082弗拉格 矩阵乘法的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 2875 [Noi2012]随机数生成器 矩阵乘法