【bzoj5082】弗拉格 矩陣乘法
阿新 • • 發佈:2017-11-23
algorithm ret 染色 輸出 git led style 顏色 狀態
題目描述
給你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】弗拉格 矩陣乘法