题意:n × n的矩阵,每个位置可以被染成黑/白色。
一种gay的染色是任意相邻两行的元素,每两个要么都相同,要么都不同。列同理。
一种gaygay的染色是一种gay的染色,其中没有哪个颜色的子矩阵大小大于等于k。
求有多少种gaygay的染色。
解:首先手玩这个gay的染色到底是什么情况。
然后发现,每种gay的染色都一一对应一种只有第一行第一列的染色。换句话说,决定了第一行第一列,就可以得到唯一一种gay的染色,并且每种gay的染色都可以用这种方式得到。
然后考虑gaygay的染色,显然,找出第一行最长连续段和第一列最长连续段。这两者的长度乘积 < k即可。
又由于第一行和第一列有一个公共元素,而黑白又是轮换对称的,所以把第一个格子固定为黑色,最后×2即可。
DP求出一行的最长连续段恰好为k时方案数。然后前缀和就是最长连续段小于等于k的方案数了。
枚举第一行的最长连续段,可以得到一个第一列最长连续段的限制。方案数乘起来就行了。
注意这个限制要对n取min。
DP这个环节,具体来说,f[i][j][1/0]表示前i个,最大连续段为j,当前有没有长度为j的连续段的方案数。刷表法转移,枚举当前放长为多少的连续段即可。复杂度n3。
1 #include <cstdio> 2 #include <algorithm> 3 4 const int N = 510, MO = 998244353; 5 6 int f[N][N][2], sum[N]; 7 8 inline void add(int &a, const int &b) { 9 a = (a + b) % MO; 10 return; 11 } 12 13 int main() { 14 int n, k; 15 scanf("%d%d", &n, &k); 16 for(int j = 1; j <= n; j++) { 17 f[0][j][0] = 1; 18 } 19 for(int i = 0; i <= n; i++) { 20 for(int j = 1; j <= n; j++) { 21 // f[i][j][0/1] 22 for(int k = 1; k < j && i + k <= n; k++) { 23 add(f[i + k][j][0], f[i][j][0]); 24 add(f[i + k][j][1], f[i][j][1]); 25 } 26 if(i + j <= n) { 27 add(f[i + j][j][1], f[i][j][0]); 28 add(f[i + j][j][1], f[i][j][1]); 29 } 30 } 31 } 32 // f[n][i][1] 33 for(int i = 1; i <= n; i++) { 34 sum[i] = (f[n][i][1] + sum[i - 1]) % MO; 35 } 36 37 int ans = 0; 38 for(int i = 1; i <= n; i++) { 39 int t = std::min(n, (k - 1) / i); 40 add(ans, 1ll * f[n][i][1] * sum[t] % MO); 41 } 42 43 printf("%d", ans * 2 % MO); 44 return 0; 45 }