VP 的时候没题可跟了,就开了这题切掉了,结果 VP 结束发现正赛就一个队伍过了???
若 \(x\) 用缩小枪击中了 \(y\),则从 \(x\) 向 \(y\) 连一条有向边。注意到,任何一个时刻得到的图是若干条链的集合,且每条链的任何一个后缀都恰好覆盖了一段连续区间。
设 \(f_{l,r}\) 表示用一条链覆盖 \([l,r]\) 区间的方案数(不考虑开枪顺序)。则有一个显然的区间 DP:
接下来考虑对于一条确定的链(设链上有 \(len\) 个人),有多少种合法的开枪顺序可以得到这条链。
显然,若存在一条链 \(x\to y\to z\),则 \(y\) 必须在 \(x\) 前开枪,否则 \(y\) 被 \(x\) 缩小了就无法开枪了。因此,每条链除了终点以外所有点的顺序都是确定的。接下来考察终点可以放在哪里。
一种情况是终点是 \(1\) 且 \(s_1=\texttt{L}\)(或者对称地,终点是 \(n\) 且 \(s_n=\texttt{R}\))。这种情况下他是否开枪、在何时开枪都不会影响结果,因为他不会击中任何人。此时合法顺序共有 \(len\) 种。
剩余情况都是等价的。一旦终点开枪,一定会击中某个人,导致链的形态改变。因此终点必须先被击中,此时合法顺序共有 \(len-1\) 种。
我们略微改变 \(f_{l,r}\) 的定义和转移,使得可以区分这两种情况:
设 \(g_{l,r}\) 表示用一条链覆盖 \([l,r]\) 区间的开枪顺序数,由刚才的讨论显然有:
设 \(h_{i,j}\) 表示用 \(j\) 条链覆盖 \([1,i]\) 前缀的开枪顺序数。枚举最后一条链覆盖的区间 \([k+1,j]\),显然不同链的操作相互独立,因此可以从 \(i\) 个位置中任选 \(i-k\) 个位置给最后一条链的人开枪,从而有:
显然,一个人未被缩小,当且仅当他是一条链的起点。因此有 \(j\) 条链就意味着有 \(j\) 个人未被缩小,\(h_{n,j}\) 即为答案。
时间复杂度 \(O(n^3)\)。
#include <bits/stdc++.h>
#define rep(i, x, y) for(int i = (x); i <= (y); ++i)
#define per(i, x, y) for(int i = (x); i >= (y); --i)
#define endl '\n'
using namespace std;const int N = 505, mod = 998244353;unsigned int down(unsigned int x) {return x >= mod ? x - mod : x;
}struct mint {unsigned int x;mint() = default;mint(unsigned int x): x(x) {}friend istream& operator>>(istream& in, mint& a) {return in >> a.x;}friend ostream& operator<<(ostream& out, mint a) {return out << a.x;}friend mint operator+(mint a, mint b) {return down(a.x + b.x);}friend mint operator-(mint a, mint b) {return down(a.x - b.x + mod);}friend mint operator*(mint a, mint b) {return 1ULL * a.x * b.x % mod;}friend mint operator/(mint a, mint b) {return a * ~b;}friend mint operator^(mint a, int b) {mint ans = 1; for(; b; b >>= 1, a *= a) if(b & 1) ans *= a; return ans;}friend mint operator~(mint a) {return a ^ (mod - 2);}friend mint operator-(mint a) {return down(mod - a.x);}friend mint& operator+=(mint& a, mint b) {return a = a + b;}friend mint& operator-=(mint& a, mint b) {return a = a - b;}friend mint& operator*=(mint& a, mint b) {return a = a * b;}friend mint& operator/=(mint& a, mint b) {return a = a / b;}friend mint& operator^=(mint& a, int b) {return a = a ^ b;}friend mint& operator++(mint& a) {return a += 1;}friend mint operator++(mint& a, int) {mint x = a; ++a; return x;}friend mint& operator--(mint& a) {return a -= 1;}friend mint operator--(mint& a, int) {mint x = a; --a; return x;}friend bool operator==(mint a, mint b) {return a.x == b.x;}friend bool operator!=(mint a, mint b) {return !(a == b);}
};int n;
string s;
mint fac[N], inv[N], ifac[N], g[N][N], h[N][N];
complex<mint> f[N][N];mint C(int x, int y) {if(x < 0 || y < 0 || x < y) return 0;else return fac[x] * ifac[y] * ifac[x - y];
}int main() {ios::sync_with_stdio(false);cin.tie(0);cin >> n >> s;s = " " + s + " ";fac[0] = fac[1] = inv[0] = inv[1] = ifac[0] = ifac[1] = 1;rep(i, 2, n) {fac[i] = fac[i - 1] * i;inv[i] = (mod - mod / i) * inv[mod % i];ifac[i] = ifac[i - 1] * inv[i];}rep(i, 1, n) {if(i == 1 && s[i] == 'L') f[i][i] = complex<mint>(0, 1);else if(i == n && s[n] == 'R') f[i][i] = complex<mint>(0, 1);else f[i][i] = complex<mint>(1, 0);}rep(len, 2, n) {rep(l, 1, n - len + 1) {int r = l + len - 1;if(s[l] == 'R') f[l][r] += f[l + 1][r];if(s[r] == 'L') f[l][r] += f[l][r - 1];}}rep(len, 1, n) {rep(l, 1, n - len + 1) {int r = l + len - 1;g[l][r] = f[l][r].real() * (len - 1) + f[l][r].imag() * len;}}h[0][0] = 1;rep(i, 1, n) {rep(j, 1, i) {rep(k, 0, i - 1) {h[i][j] += h[k][j - 1] * g[k + 1][i] * C(i, k);}}}rep(j, 1, n) cout << h[n][j] << " \n"[j == n];return 0;
}