题解:P13573 [CCPC 2024 重庆站] Pico Park

VP 的时候没题可跟了,就开了这题切掉了,结果 VP 结束发现正赛就一个队伍过了???

\(x\) 用缩小枪击中了 \(y\),则从 \(x\)\(y\) 连一条有向边。注意到,任何一个时刻得到的图是若干条链的集合,且每条链的任何一个后缀都恰好覆盖了一段连续区间。

\(f_{l,r}\) 表示用一条链覆盖 \([l,r]\) 区间的方案数(不考虑开枪顺序)。则有一个显然的区间 DP:

\[f_{l,r}= \begin{cases} 1,&l=r\\ [s_l=\texttt{R}]f_{l+1,r}+[s_r=\texttt{L}]f_{l,r-1},&l<r \end{cases} \]

接下来考虑对于一条确定的链(设链上有 \(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}\) 的定义和转移,使得可以区分这两种情况:

\[f_{l,r}= \begin{cases} \textrm{i},&l=r\land\text{第一种情况}\\ 1,&l=r\land\text{第二种情况}\\ [s_l=\texttt{R}]f_{l+1,r}+[s_r=\texttt{L}]f_{l,r-1},&l<r \end{cases} \]

\(g_{l,r}\) 表示用一条链覆盖 \([l,r]\) 区间的开枪顺序数,由刚才的讨论显然有:

\[g_{l,r}=\operatorname{Re}(f_{l,r})\times(r-l)+\operatorname{Im}(f_{l,r})\times(r-l+1) \]

\(h_{i,j}\) 表示用 \(j\) 条链覆盖 \([1,i]\) 前缀的开枪顺序数。枚举最后一条链覆盖的区间 \([k+1,i]\),显然不同链的操作相互独立,因此可以从 \(i\) 个位置中任选 \(i-k\) 个位置给最后一条链的人开枪,从而有:

\[\begin{aligned} h_{0,0}&=1\\ h_{i,j}&=\sum_{k=0}^{i-1}\binom{i}{k}h_{k,j-1}g_{k+1,i} \end{aligned} \]

显然,一个人未被缩小,当且仅当他是一条链的起点。因此有 \(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;
}
posted @ 2025-11-14 21:05  rui_er  阅读(16)  评论(0)    收藏  举报