ATB279G At Most 2 Colors 学习笔记
ATB279G At Most 2 Colors 学习笔记
前言
参考文献:这位巨佬的题解
题意简述
现在有一行 \(N\) 个格子和 \(C\) 种颜色。每一个格子都需涂 \(C\) 种颜色中的一种,并且任意相邻的 \(K\) 个格子最多有两种不同的颜色。
问有多少种染色方案。答案对 \(998244353\) 取模。
\(2\le K\le N\le 10^6,1\le C\le 10^9\)。
做法解析
看到一行上相邻格子这种玩意不难往连续段DP方向想。设 \(f_{i,j}\) 表示前 \(i\) 个格子中,\([j,i]\) 颜色相同,\(A_{j-1},A_j\) 相异的合法方案数。
那么转移方程呢?有:
\[f_{i,j}=f_{i-1,j}[j<i]
\]
\[f_{i,i}=\big(\sum_{j=i-k+2}^{i-1} f_{i-1,j}\big)+\big(\sum_{j=1}^{i-k+1}f_{i-1,j}\times (c-1)\big)
\]
怎么理解这两个式子呢?作为连续段dp,我们肯定要按照新开一段与否讨论。
如果不新开一个颜色段,也就是 \(A_i=A_{i-1}\) 时,\(f_{i,j}\) 就直接继承了 \(f_{i,j-1}\)——我往这长为 \(K\) 的窗口里多塞一个和原来段尾相同的颜色,合法性一定有保证。
如果新开一个颜色段,我们又要分两种情况讨论:
- 如果之前的那个颜色段足够长,也就是有 \(i-j'+1\ge K\),即 \(j'\le i-K+1\) 时,新的窗口中只会有那一大段 \(A_{i-1}\) 色和一个 \(A_i\) 色。那么 \(A_i\) 的颜色就有 \(c-1\) 种选择。对应的就是上文 \(\sum_{j=1}^{i-k+1}f_{i-1,j}\times (c-1)\)。
- 如果之前的那个颜色段不够长,也就是说 \(j'>i-K+1\),那么当前的窗口最左端就还有异于 \(A_{i-1}\) 的一种颜色,\(A_i\) 只能涂上这种颜色。对应的就是上文 \(\sum_{j=i-k+2}^{i-1} f_{i-1,j}\)。
好了我们已经会 \(N^2\) 做法了。我们又发现 \(f\) 的第一维完全可以通过前缀和去掉。然后就 \(O(N)\) 了!
代码实现
这个前缀和似乎和平时见到的不太一样……?
这里的下标到底是原来的 \(i\) 还是 \(j\)?
实际上,你再仔细看看上面那个式子,你就会发现所有 \(j\neq i\) 的状态都可以等于那个 \(j=i\) 的状态,所以我们只用记录所有 \(f_{i,i}\) 就够了,也就是说只有一维下标是有用的,所以这里只记录一维。
#include <bits/stdc++.h>
using namespace std;
namespace obasic{
typedef long long lolo;
template <typename _T>
void readi(_T &x){
_T k=1;x=0;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')k=-1;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-'0';
x*=k;return;
}
template <typename _T>
void writi(_T x){
if(x<0)putchar('-'),x=-x;
if(x>9)writi(x/10);
putchar(x%10+'0');
}
};
using namespace obasic;
const int MaxN=1e6+5,Mod=998244353;
lolo N,K,C,f[MaxN],g[MaxN];
int main(){
readi(N),readi(K),readi(C);
f[1]=g[1]=C;
for(int i=2;i<=N;i++){
lolo x=max(i-K+1,1ll);
f[i]=(g[i-1]-g[x]+g[x]*(C-1)%Mod+Mod)%Mod;
g[i]=(g[i-1]+f[i])%Mod;
}
writi(g[N]);
return 0;
}
反思总结
善于观察式子,可能只有一维下标有用。
见到一个序列填东西,连续多少有限制,考虑连续段dp。
浙公网安备 33010602011771号