atc abc468G 思路分享(组合数学)

https://atcoder.jp/contests/abc468/tasks/abc468_g

题意

给定长为 \(n\) 的字符串 \(s\),只包含 ox.

排列 \(P\) 满足:对 \(k=1,2,\cdots,n\),若 \(s_k=o\),则 \(P\) 中存在连续子序列是 \((1,2,\cdots,k)\) 的排列;若 \(s_k=x\),则不存在.

求符合条件的 \(P\) 的数量,模 \(998244353\).

\(1\le n \le 2000\).

思路

记所有 \(s_i=o\) 的位置为 \(p_1,p_2,\cdots, p_m\).

对于 \(p_{i-1}\to p_i\),要求填入 \([p_{i-1}+1,p_i-1]\) 时不产生新的排列,填入 \(p_i\) 时恰好产生第一个新的排列.

\(d=p_i-p_{i-1}\)\(f_j\) 表示填入第 \(j\) 个数后第一次产生新排列的方案数,求 \(f_d\).

不考虑不产生新排列的限制,方案数是 \((d+1)!\),即对前面的整块与新增 \(d\) 个元素重排.

按第一次产生新排列的位置讨论,有:

\[(d+1)! = \sum_{j=1}^{d}{f_j\cdot (d-j+1)!} \]

将第 \(d\) 项提出,整理得:

\[f_d = (d+1)!-\sum_{j=1}^{d-1}{f_j\cdot(d-j+1)!} \]

因此可以递推求得所有 \(f\),时间复杂度 \(\mathcal{O}(n^2)\).

代码

//author:kzssCCC

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

template<int MOD>
struct modint {
    int val;
    
    modint() : val(0) {}
    modint(long long v) {
        val = v % MOD;
        if (val < 0) val += MOD;
    }
    
    modint& operator++() { val = (val + 1 == MOD ? 0 : val + 1); return *this; }
    modint& operator--() { val = (val == 0 ? MOD - 1 : val - 1); return *this; }
    modint operator++(int) { modint res = *this; ++*this; return res; }
    modint operator--(int) { modint res = *this; --*this; return res; }
    
    modint& operator+=(const modint& o) { val += o.val; if (val >= MOD) val -= MOD; return *this; }
    modint& operator-=(const modint& o) { val -= o.val; if (val < 0) val += MOD; return *this; }
    modint& operator*=(const modint& o) { val = 1LL * val * o.val % MOD; return *this; }
    modint& operator/=(const modint& o) { return *this *= o.inv(); }
    
    friend modint operator+(modint a, const modint& b) { return a += b; }
    friend modint operator-(modint a, const modint& b) { return a -= b; }
    friend modint operator*(modint a, const modint& b) { return a *= b; }
    friend modint operator/(modint a, const modint& b) { return a /= b; }

    friend bool operator==(const modint& a, const modint& b) { return a.val == b.val; }
    friend bool operator!=(const modint& a, const modint& b) { return a.val != b.val; }
    modint operator-() const { modint res = *this; res.val = (res.val == 0 ? 0 : MOD - res.val); return res;};
    modint operator+() const { return *this; };
    
    modint qpow(long long p) const {
        modint res = 1, a = *this;
        while (p > 0) {
            if (p & 1) res *= a;
            a *= a;
            p >>= 1;
        }
        return res;
    }

    modint inv() const {
        return qpow(MOD - 2);
    }
    
    friend std::ostream& operator<<(std::ostream& os, const modint& m) { return os << m.val; }
    friend std::istream& operator>>(std::istream& is, modint& m) { long long v; is >> v; m = modint(v); return is; }
};
using Z = modint<998244353>;

const int MAXN = 2000;
Z fac[MAXN+2],f[MAXN+1];

void init(){
    fac[0] = 1;
    for (int i=1;i<=MAXN+1;i++){
        fac[i] = fac[i-1]*i;
    }

    for (int i=1;i<=MAXN;i++){
        f[i] = fac[i+1];
        for (int j=1;j<i;j++){
            f[i] -= f[j]*fac[i-j+1];
        }
    }
}

void solve(){
    int n;
    cin >> n;
    string s;
    cin >> s;
    s = ' '+s;

    if (s[1]=='x' || s[n]=='x'){
        cout << 0 << '\n';
        return;
    }

    vector<int> p;
    for (int i=1;i<=n;i++){
        if (s[i]=='o'){
            p.push_back(i);
        }
    }

    int len = p.size();
    Z res = 1;
    for (int i=1;i<len;i++){
        int d = p[i]-p[i-1];
        res *= f[d];
    }
    cout << res << '\n';
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);

    init();
    
    int t = 1;
    // cin >> t;
    while (t--) solve();

    return 0;
}
posted @ 2026-07-28 14:44  kzssCCC  阅读(7)  评论(0)    收藏  举报