标签:表示 content mes main pre stat highlight 复杂度 前缀
题目描述
输入
输出
输出答案
样例输入
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; }
标签:表示 content mes main pre stat highlight 复杂度 前缀
原文地址:http://www.cnblogs.com/GXZlegend/p/7886580.html