前言
- 记录一下,给自己长长记性,刚看到题目立马想出了一个 O ( T ∗ n k ) O(T*nk) O(T∗nk)的暴力DP,想着构造个矩阵用快速幂优化为 O ( ∑ 1 T k ∗ l o g ( n ) ) O(\sum_1^T{k}*log(n)) O(∑1Tk∗log(n)),结果构造一个小时发现貌似不太行,想假了,悲(
- 赛后做其他组合数的题突然想到这个题貌似也可以用组合数做,然后很快的就切了,以此题解铭记我坐了 3h 的牢
- 本题解只提供思路讲解和核心代码,其余代码需要自己实现
正文
- 题目链接
方格染色
小 M 正在处理染色问题!问题发生在一个由 2 × n 个 1 × 1 的小正方形方格组成的矩形纸条上,小 M 想要把矩形纸条的每个方格染成黑色或是白色。 但由于小 M 的视力不太好,若相邻两个方格都是黑色,小 M 会非常困惑。因此。他希望最后染色方 案中任意两个黑色方格均不相邻。同时,他还想知道在黑色方格恰好有 k 个的情况下,求出满足上述条 件的染色方案数对 998244353 取模的结果?
多测, T ≤ 1 0 5 , 1 ≤ n ≤ 1 0 5 , 0 ≤ k ≤ 1 0 5 T \le 10^5,1\le n \le 10^5,0 \le k \le 10^5 T≤105,1≤n≤105,0≤k≤105,保证 ∑ k ≤ 5 ∗ 1 0 5 \sum{k} \le 5 * 10^5 ∑k≤5∗105
思路讲解
- 用组合数做这个题,首先需要发现一个性质,如果我们把每一列看作是一个单独的整体
- 定义:我们把相邻列有黑色块的列看作是一个块 \color{Orange}定义:我们把相邻列有黑色块的列看作是一个块 定义:我们把相邻列有黑色块的列看作是一个块
- 那么这一个块内的不同摆放只有两种
-
发现这个性质后,我们可以考虑枚举一共有多少个块,然后把这些块拿出来,现在还剩
n-k
列,我们需要做的就是把这些块插到这n-k
列中,然后形成的方案数就是答案了 -
那么怎么求方案数呢,这里先给出公式 ∑ d = 1 k C ( k − 1 , d − 1 ) ∗ C ( n − k + 1 , d ) ∗ 2 d \sum_{d=1}^k C(k-1,d-1)*C(n-k+1,d)*2^d ∑d=1kC(k−1,d−1)∗C(n−k+1,d)∗2d
-
这个公式是怎么推出来的呢?
-
首先我们枚举
n
列方格一共有多少的块,然后怎么求这些块形成的方案数呢,对于块数d
,我们要做的就是把k
列分成d
个块,采用隔板法,即:现在有k
个小球,你要在k
个小球的之间放置d-1
个隔板,使得分成d
个非空集合,由于k
个小球的缝的个数是k-1
个(两边不可以插入),那么形成的方案应该是 C ( k − 1 , d − 1 ) C(k-1,d-1) C(k−1,d−1) -
由于每个块是不能相邻的(否则他们应该是一个块),现在需要把形成的
d
个块插入到原来的白色列的缝隙里,白色列的个数是n-k
,那么缝数是n-k+1
(两边可以插入),那么方案数就是 C ( n − k + 1 , d ) C(n-k+1,d) C(n−k+1,d) -
对于每个块有两种摆放方式,那么
d
个块有 2 d 2^d 2d 种摆放方式 -
之后我们只需要把不同的
d
的方案数累计起来即可,在不考虑取模的情况下时间复杂度为 O ( ∑ 1 T k ) O(\sum_1^T k) O(∑1Tk)
Code
void solve(int Case) {
int n, k;
cin >> n >> k;
if (k == 0) { // 只有什么都不放一种方案
cout << "1\n";
return;
}
if (n < k) { // k太大,放不下
cout << "0\n";
return;
}
modint ans = 0;
for (int d = 1; d <= k; d++) {
ans += C(k - 1, d - 1) * C(n - k + 1, d) * modint(2).pow(d);
}
cout << ans << "\n";
}