CF1027E Inverse Coloring

题意: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 }
AC代码

 

posted @ 2019-02-03 21:33  huyufeifei  阅读(274)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜