4.30 CW 模拟赛 T1. 序列
前言
先自己看, 不行借助题解
稍微有点心态
思路
首先计数问题, 考虑转化到线性上
尝试刻画一下最终的序列
有以下观察\((\)并不能简单做出\()\)
- 一个完整段的前后分别是一些前缀和一些后缀
- 后缀和前缀中间可以加上一个任意段
不难发现用这样两个部分可以表示任意序列, 注意可能有位置不被覆盖
这时我们可以把问题转化为对这样序列的计数
考虑算重的问题, 不难发现我们只统计压着别的覆盖的覆盖或者对应位置与 \(a_i\) 不同的覆盖即可
由此, 状态可以分为 \(4\) 类
): 上次覆盖到 \(i\), 以 \(< m\) 结尾F]: 上次覆盖到 \(i\), 以 \(m\) 结尾, 且其已经压着别的覆盖, 或者它对应 \(a_i\) 的部分已经不同于 \(a_i\)T]: 上次覆盖到 \(i\), 以 \(m\) 结尾, 且其仍未压着别的覆盖, 而且它对应 \(a_i\) 的部分与 \(a_i\) 相同o: 上次没有覆盖到 \(i\), 使用 \(a_i\) 代替
注意到 ) 后面的覆盖一定是压着的
但是 ] 后面不一定
) -> [ )
[ ](F)
o -> o
[ ](根据 a[i] 决定 T/F)
[ )(要求不同于 a[i])
F]-> o
[ ](根据 a[i] 决定 T/F)
( ](根据 a[i] 决定 T/F)
[ )(要求不同于 a[i])
( )(要求不同于 a[i])
T]-> o
( )(要求不同于 a[i])
( ](根据 a[i] 决定 T/F)
只给转移或许并不是很好理解
下面做出进一步的解释及推导
首先, 任意序列一定可以如下构造
\[\underbrace{[\hspace{1cm})[\hspace{0.6cm})[\hspace{1.2cm})}_{一些前缀(可以为空)}
\underbrace{[\hspace{2cm}]}_{一个完整段(必须有)}
\underbrace{(\hspace{0.6cm}](\hspace{1cm}](\hspace{0.4cm}]}_{一些后缀(可以为空)}
\underbrace{(\hspace{0.8cm})}_{一个任意段}
[\hspace{0.6cm})[\hspace{0.4cm})[\hspace{0.8cm})[\hspace{2cm}](\hspace{0.6cm}]
\]
特别的
]之后可以放置o表示不覆盖]之后放置的)一定不会压着别的覆盖, 故要求与 \(a_i\) 不同- 放置
( )时注意需要区分每种固定长度为 \(l\) 的连续段, 因此方案数为 \(\max(0, m - l - 1)\), 还需要注意如果存在和 \(a_i\) 相同的连续段, 则方案数减 \(1\) - 判断每种区间可以维护一个标签, 表示这个区间是否连续递增 \(1\), 然后判断一下起点终点即可知道这个区间的形式
注意到我们可以通过刻画 ), ], o 对应的后一个区间类型来表示一个区间
引入算重
注意到我们需要知道一个区间是否压着别的覆盖
因为我们是从左往右考虑的, 所以我们只需要知道这个区间的后一个区间是否压着别的覆盖即可
注意到 ) 后面的覆盖一定是压着的, 但是 ] 后面不一定
于是强行设 T] 表示当前他还没压着而且对应 \(a_i\) 的部分与 \(a_i\) 相同, 这一部分的方案数只有可能在他压着后面的区间的时候出现贡献
另外设 F] 表示当前他压着别的覆盖或者对应 \(a_i\) 的部分与 \(a_i\) 不同, 这一部分的方案数可以按照意义转移
给一份参考代码
参考代码 (from yukimianyan)
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template <unsigned umod>
struct modint {/*{{{*/
static constexpr int mod = umod;
unsigned v;
modint() = default;
template <class T, enable_if_t<is_integral<T>::value, int> = 0>
modint(const T& y) : v(y % mod + (is_signed<T>() && y < 0 ? mod : 0)) {}
modint& operator+=(const modint& rhs) { v += rhs.v; if (v >= umod) v -= umod; return *this; }
modint& operator-=(const modint& rhs) { v -= rhs.v; if (v >= umod) v += umod; return *this; }
modint& operator*=(const modint& rhs) { v = (unsigned)(1ull * v * rhs.v % umod); return *this; }
modint& operator/=(const modint& rhs) { assert(rhs.v); return *this *= qpow(rhs, mod - 2); }
friend modint operator+(modint lhs, const modint& rhs) { return lhs += rhs; }
friend modint operator-(modint lhs, const modint& rhs) { return lhs -= rhs; }
friend modint operator*(modint lhs, const modint& rhs) { return lhs *= rhs; }
friend modint operator/(modint lhs, const modint& rhs) { return lhs /= rhs; }
template <class T> friend modint qpow(modint a, T b) {
modint r = 1;
for (assert(b >= 0); b; b >>= 1, a *= a) if (b & 1) r *= a;
return r;
}
friend int raw(const modint& self) { return self.v; }
friend ostream& operator<<(ostream& os, const modint& self) { return os << raw(self); }
explicit operator bool() const { return v != 0; }
};/*}}}*/
using mint = modint<998244353>;
int n, m, a[1000010];
mint f[1000010][4];
int main() {
#ifndef LOCAL
#ifndef NF
freopen("string.in", "r", stdin);
freopen("string.out", "w", stdout);
#endif
cin.tie(nullptr)->sync_with_stdio(false);
#endif
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
f[0][2] = 1;
for (int i = 0; i < n; i++) {
bool flag = true;
f[i + 1][2] += f[i][2] + f[i][0];
for (int j = 1; j <= min(m, n - i); j++) {
if (j > 1) flag &= a[i + j] - 1 == a[i + j - 1];
if (j == m) {
f[i + j][0] += f[i][1];
f[i + j][flag && a[i + j] == m ? 3 : 0] += f[i][2] + f[i][0];
} else {
f[i + j][1] += f[i][1];
if (!flag || a[i + 1] != 1) f[i + j][1] += f[i][2] + f[i][0];
f[i + j][flag && a[i + j] == m ? 3 : 0] += f[i][0] + f[i][3];
f[i + j][1] += (f[i][3] + f[i][0]) * max(0, m - j - 1 - (flag && 1 < a[i + 1] && a[i + j] < m));
}
}
}
for (int i = 1; i <= n; i++) debug("f[%d] = {%d, %d, %d, %d}\n", i, raw(f[i][0]), raw(f[i][1]), raw(f[i][2]), raw(f[i][3]));
cout << f[n][0] + f[n][2] << endl;
return 0;
}
总结
线性计数多半是要找构造的
考虑这种给你一个不为 \(0\) 的原串让你做操作的, 往往需要找可以被原串替代的形式, 注意可能的误区
那个啥, 查重过高可能需要把原文放出来


浙公网安备 33010602011771号