[Easy-Medium] CF1989E Distance to Different
2025/2/2约 708 字大约 4 分钟
简要题意
对于一个长度为 的序列 ,定义 的生成序列 为一个长度为 的序列,且满足:
我们称一个序列是好的,当且仅当值域恰好为 。你需要求所有长度为 的好的序列的生成序列数量。相同的序列仅计算一次。
。
思路
先来考虑长度为 的序列的生成序列数量,由于 的意义其实是 到最近的与 不同的 的距离,所以对于一个长度为 的颜色段, 应当形如 。特别地,若颜色段位于开头或结尾,则为 以及 。这启示我们将 划分为若干段,则每一段对应 的颜色段,且 中该段的填数只和 和是否位于开头结尾有关。
dp,设 表示长度为 的序列的生成序列数量,不难有转移:
这样会算重,原因是对于不为首尾的长度为 的段,则形如 ,和两个长度为 的段一致,所以需要排除这种情况:
用前缀和优化转移,时间复杂度 。
然后考虑值域的限制,只需要将序列分成至少 段即可,只需要在 的基础上加一维表示划分的段数即可。
时间复杂度 ,考虑优化,改为用全集减去分成少于 段的方案即可做到 。
代码
#include <bits/stdc++.h>
using namespace std;
constexpr int mod = 998244353;
int Add(int x, int y){ return (x + y) >= mod ? (x + y - mod) : (x + y); }
int Sub(int x, int y){ return (x - y) < 0 ? (x - y + mod) : (x - y); }
int Mul(int x, int y){ return 1ll * x * y % mod; }
const int N = 2e5 + 5;
int n, k, dp[N], f[N][15], g[N][15], sumt[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n >> k;
dp[0] = sumt[0] = 1;
for(int i=1;i<=n;i++){
dp[i] = sumt[i - 1];
if(i >= 2 && i != 2 && i != n) dp[i] = Sub(dp[i], dp[i - 2]);
sumt[i] = Add(sumt[i - 1], dp[i]);
}
f[0][0] = g[0][0] = 1;
for(int i=1;i<=n;i++){
g[i][0] = g[i - 1][0];
for(int j=1;j<=min(k-1,i);j++){
f[i][j] = g[i - 1][j - 1];
if(i >= 2 && i != 2 && i != n) f[i][j] = Sub(f[i][j], f[i - 2][j - 1]);
g[i][j] = Add(g[i - 1][j], f[i][j]);
}
}
int ans = 0;
for(int i=1;i<k;i++) ans = Add(ans, f[n][i]);
cout << Sub(dp[n], ans) << '\n';
return 0;
}
// Written by xiezheyuan