1 条题解

  • 0
    @ 2026-5-19 20:35:50

    问题重述

    给定长度为 nn 的排列 pp,求有多少个排列 qq 满足:对于所有 1i<n1 \le i < n
    max(q1,,qi)max(p1,,pi). \max(q_1,\dots,q_i) \neq \max(p_1,\dots,p_i).
    答案对 998244353998244353 取模。


    关键转化

    ai=max(p1,,pi)a_i = \max(p_1,\dots,p_i),则 a1a2an=na_1 \le a_2 \le \dots \le a_n = n,且 aiia_i \ge i(因为前 ii 个数最大值至少为 ii)。
    对于排列 qq,定义 Ni=max(q1,,qi)N_i = \max(q_1,\dots,q_i),则 0=N0<N1<<Nn=n0 = N_0 < N_1 < \dots < N_n = n(严格递增)。
    条件要求 NiaiN_i \neq a_i 对所有 1in11 \le i \le n-1 成立。

    因此问题转化为:计数严格递增序列 0=N0<N1<<Nn=n0=N_0<N_1<\dots<N_n=n,使得 NiaiN_i \neq a_i1in11\le i\le n-1)。
    每个这样的序列对应一组 qq,具体对应关系为:在 NiN_i 处必须放置 NiN_i 本身,其余位置(非前缀最大值的位置)可以任意排列剩余数字。因此方案数为
    i=1n1(Ni+1Ni1)!, \prod_{i=1}^{n-1} (N_{i+1} - N_i - 1)!, 其中 Ni+1Ni1N_{i+1}-N_i-1 是介于两个相邻最大值之间的数字个数。


    动态规划与分治 NTT

    直接枚举 NiN_i 不可行,但可以利用 aia_i 的结构进行 DP。
    定义 dp[i]dp[i] 表示已经决定了 N1,,NiN_1,\dots,N_iNi=aiN_i = a_i 时的某种累加值(实际上标程中的 dp[i]dp[i] 是经过阶乘逆元调整后的量)。
    转移考虑从 j<ij < iaj<aia_j < a_i 的位置转移,中间需要插入 aiaj1a_i - a_j - 1 个数字,这些数字可以任意排列,贡献阶乘因子。另外当 aj=aia_j = a_i(即 aa 值不变)时,需要特殊处理。

    标程使用分治 + NTT 优化卷积形如
    jdp[j]fac[aij1] \sum_{j} dp[j] \cdot \text{fac}[a_i - j - 1]
    的转移,并对“平台”(连续相同 aa 值)进行线性处理。

    最终答案为 dp[n]dp[n](已包含所有阶乘逆元)。


    算法步骤

    1. 读入 pp,计算 ai=max(ai1,pi)a_i = \max(a_{i-1}, p_i),其中 a0=0a_0 = 0
    2. 预处理阶乘 fac 和逆阶乘 ifacn+1n+1
    3. 定义数组 dpaux,长度 n+1n+1
    4. 递归分治 solve(l, r)
      • l+1=rl+1 = r
        • l=0l = 0,则 dp[l]=1dp[l] = -1
        • 否则 dp[l]+=aux[l]ldp[l] += aux[l] \cdot l
        • 最后 dp[l]×=ifac[a[l]l]dp[l] \times= \text{ifac}[a[l] - l]
      • 否则,取中点 mid=(l+r)/2mid = \lfloor (l+r)/2 \rfloor
        • 递归左半 [l,mid)[l, mid)
        • 处理 a[mid]=a[mid1]a[mid] = a[mid-1] 的连续段(平台),用线性方法更新。
        • 构建向量:
          • vec[k]=fac[k]vec[k] = \text{fac}[k]kk00a[r1](l+1)a[r-1] - (l+1)
          • vec1[i]=dp[i]vec1[i] = dp[i]i[l,mid)i \in [l, mid)
          • vec2[i]=dp[i]ivec2[i] = dp[i] \cdot i
        • 计算卷积 res1=vecvec1res1 = vec * vec1res2=vecvec2res2 = vec * vec2(NTT)。
        • i[mid,r)i \in [mid, r),设 base=a[mid]mid+l+1base = a[mid] - mid + l + 1,若 a[i]basea[i]-baseres1res1 范围内,则: [ aux[i] -= res1[a[i]-base], \quad dp[i] += res2[a[i]-base]. ]
        • 递归右半 [mid,r)[mid, r)
    5. 答案即为 dp[n].val()dp[n].val()

    复杂度

    • 预处理 O(n)O(n)
    • 分治 O(nlog2n)O(n \log^2 n),因为每层卷积 O(nlogn)O(n \log n),共 logn\log n 层。
    • nn 不超过 2×1052\times 10^5,可接受。

    标程代码

    #include<bits/stdc++.h>
    using namespace std;
    
    #define all(a) a.begin(),a.end()
    #define pb push_back
    #define sz(a) ((int)a.size())
    
    using ll=long long;
    using u32=unsigned int;
    using u64=unsigned long long;
    using i128=__int128;
    using u128=unsigned __int128;
    using f128=__float128;
    
    using pii=pair<int,int>;
    using pll=pair<ll,ll>;
    
    template<typename T> using vc=vector<T>;
    template<typename T> using vvc=vc<vc<T>>;
    template<typename T> using vvvc=vc<vvc<T>>;
    
    using vi=vc<int>;
    using vll=vc<ll>;
    using vvi=vc<vi>;
    using vvll=vc<vll>;
    
    #define vv(type,name,n,...) \
        vector<vector<type>> name(n,vector<type>(__VA_ARGS__))
    #define vvv(type,name,n,m,...) \
        vector<vector<vector<type>>> name(n,vector<vector<type>>(m,vector<type>(__VA_ARGS__)))
    
    template<typename T> using min_heap=priority_queue<T,vector<T>,greater<T>>;
    template<typename T> using max_heap=priority_queue<T>;
    
    // https://trap.jp/post/1224/
    #define rep1(n) for(ll i=0; i<(ll)(n); ++i)
    #define rep2(i,n) for(ll i=0; i<(ll)(n); ++i)
    #define rep3(i,a,b) for(ll i=(ll)(a); i<(ll)(b); ++i)
    #define rep4(i,a,b,c) for(ll i=(ll)(a); i<(ll)(b); i+=(c))
    #define cut4(a,b,c,d,e,...) e
    #define rep(...) cut4(__VA_ARGS__,rep4,rep3,rep2,rep1)(__VA_ARGS__)
    #define per1(n) for(ll i=((ll)n)-1; i>=0; --i)
    #define per2(i,n) for(ll i=((ll)n)-1; i>=0; --i)
    #define per3(i,a,b) for(ll i=((ll)a)-1; i>=(ll)(b); --i)
    #define per4(i,a,b,c) for(ll i=((ll)a)-1; i>=(ll)(b); i-=(c))
    #define per(...) cut4(__VA_ARGS__,per4,per3,per2,per1)(__VA_ARGS__)
    #define rep_subset(i,s) for(ll i=(s); i>=0; i=(i==0?-1:(i-1)&(s)))
    
    template<typename T, typename S> constexpr T ifloor(const T a, const S b){return a/b-(a%b&&(a^b)<0);}
    template<typename T, typename S> constexpr T iceil(const T a, const S b){return ifloor(a+b-1,b);}
    
    template<typename T>
    void sort_unique(vector<T> &vec){
        sort(vec.begin(),vec.end());
        vec.resize(unique(vec.begin(),vec.end())-vec.begin());
    }
    
    template<typename T, typename S> constexpr bool chmin(T &a, const S b){if(a>b) return a=b,true; return false;}
    template<typename T, typename S> constexpr bool chmax(T &a, const S b){if(a<b) return a=b,true; return false;}
    
    template<typename T, typename S> istream& operator >> (istream& i, pair<T,S> &p){return i >> p.first >> p.second;}
    template<typename T, typename S> ostream& operator << (ostream& o, const pair<T,S> &p){return o << p.first << ' ' << p.second;}
    
    #ifdef i_am_noob
    #define bug(...) cerr << "#" << __LINE__ << ' ' << #__VA_ARGS__ << "- ", _do(__VA_ARGS__)
    template<typename T> void _do(vector<T> x){for(auto i: x) cerr << i << ' ';cerr << "\n";}
    template<typename T> void _do(set<T> x){for(auto i: x) cerr << i << ' ';cerr << "\n";}
    template<typename T> void _do(unordered_set<T> x){for(auto i: x) cerr << i << ' ';cerr << "\n";}
    template<typename T> void _do(T && x) {cerr << x << endl;}
    template<typename T, typename ...S> void _do(T && x, S&&...y) {cerr << x << ", "; _do(y...);}
    #else
    #define bug(...) 777771449
    #endif
    
    template<typename T> void print(vector<T> x){for(auto i: x) cout << i << ' ';cout << "\n";}
    template<typename T> void print(set<T> x){for(auto i: x) cout << i << ' ';cout << "\n";}
    template<typename T> void print(unordered_set<T> x){for(auto i: x) cout << i << ' ';cout << "\n";}
    template<typename T> void print(T && x) {cout << x << "\n";}
    template<typename T, typename... S> void print(T && x, S&&... y) {cout << x << ' ';print(y...);}
    
    template<typename T> istream& operator >> (istream& i, vector<T> &vec){for(auto &x: vec) i >> x; return i;}
    
    vvi read_graph(int n, int m, int base=1){
        vvi adj(n);
        for(int i=0,u,v; i<m; ++i){
            cin >> u >> v,u-=base,v-=base;
            adj[u].pb(v),adj[v].pb(u);
        }
        return adj;
    }
    
    vvi read_tree(int n, int base=1){return read_graph(n,n-1,base);}
    
    template<typename T, typename S> pair<T,S> operator + (const pair<T,S> &a, const pair<T,S> &b){return {a.first+b.first,a.second+b.second};}
    
    template<typename T> constexpr T inf=0;
    template<> constexpr int inf<int> = 0x3f3f3f3f;
    template<> constexpr ll inf<ll> = 0x3f3f3f3f3f3f3f3f;
    
    template<typename T> vector<T> operator += (vector<T> &a, int val){for(auto &i: a) i+=val; return a;}
    
    template<typename T> T isqrt(const T &x){T y=sqrt(x+2); while(y*y>x) y--; return y;}
    
    #define ykh mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
    
    
    
    #include <utility>
    
    namespace atcoder {
    
    namespace internal {
    
    // @param m `1 <= m`
    // @return x mod m
    constexpr long long safe_mod(long long x, long long m) {
        x %= m;
        if (x < 0) x += m;
        return x;
    }
    
    // Fast modular multiplication by barrett reduction
    // Reference: https://en.wikipedia.org/wiki/Barrett_reduction
    // NOTE: reconsider after Ice Lake
    struct barrett {
        unsigned int _m;
        unsigned long long im;
    
        // @param m `1 <= m < 2^31`
        barrett(unsigned int m) : _m(m), im((unsigned long long)(-1) / m + 1) {}
    
        // @return m
        unsigned int umod() const { return _m; }
    
        // @param a `0 <= a < m`
        // @param b `0 <= b < m`
        // @return `a * b % m`
        unsigned int mul(unsigned int a, unsigned int b) const {
            // [1] m = 1
            // a = b = im = 0, so okay
    
            // [2] m >= 2
            // im = ceil(2^64 / m)
            // -> im * m = 2^64 + r (0 <= r < m)
            // let z = a*b = c*m + d (0 <= c, d < m)
            // a*b * im = (c*m + d) * im = c*(im*m) + d*im = c*2^64 + c*r + d*im
            // c*r + d*im < m * m + m * im < m * m + 2^64 + m <= 2^64 + m * (m + 1) < 2^64 * 2
            // ((ab * im) >> 64) == c or c + 1
            unsigned long long z = a;
            z *= b;
    #ifdef _MSC_VER
            unsigned long long x;
            _umul128(z, im, &x);
    #else
            unsigned long long x =
                (unsigned long long)(((unsigned __int128)(z)*im) >> 64);
    #endif
            unsigned int v = (unsigned int)(z - x * _m);
            if (_m <= v) v += _m;
            return v;
        }
    };
    
    // @param n `0 <= n`
    // @param m `1 <= m`
    // @return `(x ** n) % m`
    constexpr long long pow_mod_constexpr(long long x, long long n, int m) {
        if (m == 1) return 0;
        unsigned int _m = (unsigned int)(m);
        unsigned long long r = 1;
        unsigned long long y = safe_mod(x, m);
        while (n) {
            if (n & 1) r = (r * y) % _m;
            y = (y * y) % _m;
            n >>= 1;
        }
        return r;
    }
    
    // Reference:
    // M. Forisek and J. Jancina,
    // Fast Primality Testing for Integers That Fit into a Machine Word
    // @param n `0 <= n`
    constexpr bool is_prime_constexpr(int n) {
        if (n <= 1) return false;
        if (n == 2 || n == 7 || n == 61) return true;
        if (n % 2 == 0) return false;
        long long d = n - 1;
        while (d % 2 == 0) d /= 2;
        constexpr long long bases[3] = {2, 7, 61};
        for (long long a : bases) {
            long long t = d;
            long long y = pow_mod_constexpr(a, t, n);
            while (t != n - 1 && y != 1 && y != n - 1) {
                y = y * y % n;
                t <<= 1;
            }
            if (y != n - 1 && t % 2 == 0) {
                return false;
            }
        }
        return true;
    }
    template <int n> constexpr bool is_prime = is_prime_constexpr(n);
    
    // @param b `1 <= b`
    // @return pair(g, x) s.t. g = gcd(a, b), xa = g (mod b), 0 <= x < b/g
    constexpr std::pair<long long, long long> inv_gcd(long long a, long long b) {
        a = safe_mod(a, b);
        if (a == 0) return {b, 0};
    
        // Contracts:
        // [1] s - m0 * a = 0 (mod b)
        // [2] t - m1 * a = 0 (mod b)
        // [3] s * |m1| + t * |m0| <= b
        long long s = b, t = a;
        long long m0 = 0, m1 = 1;
    
        while (t) {
            long long u = s / t;
            s -= t * u;
            m0 -= m1 * u;  // |m1 * u| <= |m1| * s <= b
    
            // [3]:
            // (s - t * u) * |m1| + t * |m0 - m1 * u|
            // <= s * |m1| - t * u * |m1| + t * (|m0| + |m1| * u)
            // = s * |m1| + t * |m0| <= b
    
            auto tmp = s;
            s = t;
            t = tmp;
            tmp = m0;
            m0 = m1;
            m1 = tmp;
        }
        // by [3]: |m0| <= b/g
        // by g != b: |m0| < b/g
        if (m0 < 0) m0 += b / s;
        return {s, m0};
    }
    
    // Compile time primitive root
    // @param m must be prime
    // @return primitive root (and minimum in now)
    constexpr int primitive_root_constexpr(int m) {
        if (m == 2) return 1;
        if (m == 167772161) return 3;
        if (m == 469762049) return 3;
        if (m == 754974721) return 11;
        if (m == 998244353) return 3;
        int divs[20] = {};
        divs[0] = 2;
        int cnt = 1;
        int x = (m - 1) / 2;
        while (x % 2 == 0) x /= 2;
        for (int i = 3; (long long)(i)*i <= x; i += 2) {
            if (x % i == 0) {
                divs[cnt++] = i;
                while (x % i == 0) {
                    x /= i;
                }
            }
        }
        if (x > 1) {
            divs[cnt++] = x;
        }
        for (int g = 2;; g++) {
            bool ok = true;
            for (int i = 0; i < cnt; i++) {
                if (pow_mod_constexpr(g, (m - 1) / divs[i], m) == 1) {
                    ok = false;
                    break;
                }
            }
            if (ok) return g;
        }
    }
    template <int m> constexpr int primitive_root = primitive_root_constexpr(m);
    
    }  // namespace internal
    
    }  // namespace atcoder
    
    
    #include <cassert>
    #include <numeric>
    #include <type_traits>
    
    namespace atcoder {
    
    namespace internal {
    
    #ifndef _MSC_VER
    template <class T>
    using is_signed_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value ||
                                      std::is_same<T, __int128>::value,
                                  std::true_type,
                                  std::false_type>::type;
    
    template <class T>
    using is_unsigned_int128 =
        typename std::conditional<std::is_same<T, __uint128_t>::value ||
                                      std::is_same<T, unsigned __int128>::value,
                                  std::true_type,
                                  std::false_type>::type;
    
    template <class T>
    using make_unsigned_int128 =
        typename std::conditional<std::is_same<T, __int128_t>::value,
                                  __uint128_t,
                                  unsigned __int128>;
    
    template <class T>
    using is_integral = typename std::conditional<std::is_integral<T>::value ||
                                                      is_signed_int128<T>::value ||
                                                      is_unsigned_int128<T>::value,
                                                  std::true_type,
                                                  std::false_type>::type;
    
    template <class T>
    using is_signed_int = typename std::conditional<(is_integral<T>::value &&
                                                     std::is_signed<T>::value) ||
                                                        is_signed_int128<T>::value,
                                                    std::true_type,
                                                    std::false_type>::type;
    
    template <class T>
    using is_unsigned_int =
        typename std::conditional<(is_integral<T>::value &&
                                   std::is_unsigned<T>::value) ||
                                      is_unsigned_int128<T>::value,
                                  std::true_type,
                                  std::false_type>::type;
    
    template <class T>
    using to_unsigned = typename std::conditional<
        is_signed_int128<T>::value,
        make_unsigned_int128<T>,
        typename std::conditional<std::is_signed<T>::value,
                                  std::make_unsigned<T>,
                                  std::common_type<T>>::type>::type;
    
    #else
    
    template <class T> using is_integral = typename std::is_integral<T>;
    
    template <class T>
    using is_signed_int =
        typename std::conditional<is_integral<T>::value && std::is_signed<T>::value,
                                  std::true_type,
                                  std::false_type>::type;
    
    template <class T>
    using is_unsigned_int =
        typename std::conditional<is_integral<T>::value &&
                                      std::is_unsigned<T>::value,
                                  std::true_type,
                                  std::false_type>::type;
    
    template <class T>
    using to_unsigned = typename std::conditional<is_signed_int<T>::value,
                                                  std::make_unsigned<T>,
                                                  std::common_type<T>>::type;
    
    #endif
    
    template <class T>
    using is_signed_int_t = std::enable_if_t<is_signed_int<T>::value>;
    
    template <class T>
    using is_unsigned_int_t = std::enable_if_t<is_unsigned_int<T>::value>;
    
    template <class T> using to_unsigned_t = typename to_unsigned<T>::type;
    
    }  // namespace internal
    
    }  // namespace atcoder
    
    #include <cassert>
    #include <numeric>
    #include <type_traits>
    
    #ifdef _MSC_VER
    #include <intrin.h>
    #endif
    
    namespace atcoder {
    
    namespace internal {
    
    struct modint_base {};
    struct static_modint_base : modint_base {};
    
    template <class T> using is_modint = std::is_base_of<modint_base, T>;
    template <class T> using is_modint_t = std::enable_if_t<is_modint<T>::value>;
    
    }  // namespace internal
    
    template <int m, std::enable_if_t<(1 <= m)>* = nullptr>
    struct static_modint : internal::static_modint_base {
        using mint = static_modint;
    
      public:
        static constexpr int mod() { return m; }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
    
        static_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        static_modint(T v) {
            long long x = (long long)(v % (long long)(umod()));
            if (x < 0) x += umod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        static_modint(T v) {
            _v = (unsigned int)(v % umod());
        }
        static_modint(bool v) { _v = ((unsigned int)(v) % umod()); }
    
        unsigned int val() const { return _v; }
    
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
    
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v -= rhs._v;
            if (_v >= umod()) _v += umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            unsigned long long z = _v;
            z *= rhs._v;
            _v = (unsigned int)(z % umod());
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
    
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
    
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            if (prime) {
                assert(_v);
                return pow(umod() - 2);
            } else {
                auto eg = internal::inv_gcd(_v, m);
                assert(eg.first == 1);
                return eg.second;
            }
        }
    
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
    
      private:
        unsigned int _v;
        static constexpr unsigned int umod() { return m; }
        static constexpr bool prime = internal::is_prime<m>;
    };
    
    template <int id> struct dynamic_modint : internal::modint_base {
        using mint = dynamic_modint;
    
      public:
        static int mod() { return (int)(bt.umod()); }
        static void set_mod(int m) {
            assert(1 <= m);
            bt = internal::barrett(m);
        }
        static mint raw(int v) {
            mint x;
            x._v = v;
            return x;
        }
    
        dynamic_modint() : _v(0) {}
        template <class T, internal::is_signed_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            long long x = (long long)(v % (long long)(mod()));
            if (x < 0) x += mod();
            _v = (unsigned int)(x);
        }
        template <class T, internal::is_unsigned_int_t<T>* = nullptr>
        dynamic_modint(T v) {
            _v = (unsigned int)(v % mod());
        }
        dynamic_modint(bool v) { _v = ((unsigned int)(v) % mod()); }
    
        unsigned int val() const { return _v; }
    
        mint& operator++() {
            _v++;
            if (_v == umod()) _v = 0;
            return *this;
        }
        mint& operator--() {
            if (_v == 0) _v = umod();
            _v--;
            return *this;
        }
        mint operator++(int) {
            mint result = *this;
            ++*this;
            return result;
        }
        mint operator--(int) {
            mint result = *this;
            --*this;
            return result;
        }
    
        mint& operator+=(const mint& rhs) {
            _v += rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator-=(const mint& rhs) {
            _v += mod() - rhs._v;
            if (_v >= umod()) _v -= umod();
            return *this;
        }
        mint& operator*=(const mint& rhs) {
            _v = bt.mul(_v, rhs._v);
            return *this;
        }
        mint& operator/=(const mint& rhs) { return *this = *this * rhs.inv(); }
    
        mint operator+() const { return *this; }
        mint operator-() const { return mint() - *this; }
    
        mint pow(long long n) const {
            assert(0 <= n);
            mint x = *this, r = 1;
            while (n) {
                if (n & 1) r *= x;
                x *= x;
                n >>= 1;
            }
            return r;
        }
        mint inv() const {
            auto eg = internal::inv_gcd(_v, mod());
            assert(eg.first == 1);
            return eg.second;
        }
    
        friend mint operator+(const mint& lhs, const mint& rhs) {
            return mint(lhs) += rhs;
        }
        friend mint operator-(const mint& lhs, const mint& rhs) {
            return mint(lhs) -= rhs;
        }
        friend mint operator*(const mint& lhs, const mint& rhs) {
            return mint(lhs) *= rhs;
        }
        friend mint operator/(const mint& lhs, const mint& rhs) {
            return mint(lhs) /= rhs;
        }
        friend bool operator==(const mint& lhs, const mint& rhs) {
            return lhs._v == rhs._v;
        }
        friend bool operator!=(const mint& lhs, const mint& rhs) {
            return lhs._v != rhs._v;
        }
    
      private:
        unsigned int _v;
        static internal::barrett bt;
        static unsigned int umod() { return bt.umod(); }
    };
    template <int id> internal::barrett dynamic_modint<id>::bt = 998244353;
    
    using modint998244353 = static_modint<998244353>;
    using modint1000000007 = static_modint<1000000007>;
    using modint = dynamic_modint<-1>;
    
    namespace internal {
    
    template <class T>
    using is_static_modint = std::is_base_of<internal::static_modint_base, T>;
    
    template <class T>
    using is_static_modint_t = std::enable_if_t<is_static_modint<T>::value>;
    
    template <class> struct is_dynamic_modint : public std::false_type {};
    template <int id>
    struct is_dynamic_modint<dynamic_modint<id>> : public std::true_type {};
    
    template <class T>
    using is_dynamic_modint_t = std::enable_if_t<is_dynamic_modint<T>::value>;
    
    }  // namespace internal
    
    }  // namespace atcoder
    
    
    #include <algorithm>
    #include <array>
    
    #ifdef _MSC_VER
    #include <intrin.h>
    #endif
    
    namespace atcoder {
    
    namespace internal {
    
    // @param n `0 <= n`
    // @return minimum non-negative `x` s.t. `n <= 2**x`
    int ceil_pow2(int n) {
        int x = 0;
        while ((1U << x) < (unsigned int)(n)) x++;
        return x;
    }
    
    // @param n `1 <= n`
    // @return minimum non-negative `x` s.t. `(n & (1 << x)) != 0`
    int bsf(unsigned int n) {
    #ifdef _MSC_VER
        unsigned long index;
        _BitScanForward(&index, n);
        return index;
    #else
        return __builtin_ctz(n);
    #endif
    }
    
    }  // namespace internal
    
    }  // namespace atcoder
    
    #include <cassert>
    #include <type_traits>
    #include <vector>
    
    namespace atcoder {
    
    namespace internal {
    
    template <class mint, internal::is_static_modint_t<mint>* = nullptr>
    void butterfly(std::vector<mint>& a) {
        static constexpr int g = internal::primitive_root<mint::mod()>;
        int n = int(a.size());
        int h = internal::ceil_pow2(n);
    
        static bool first = true;
        static mint sum_e[30];  // sum_e[i] = ies[0] * ... * ies[i - 1] * es[i]
        if (first) {
            first = false;
            mint es[30], ies[30];  // es[i]^(2^(2+i)) == 1
            int cnt2 = bsf(mint::mod() - 1);
            mint e = mint(g).pow((mint::mod() - 1) >> cnt2), ie = e.inv();
            for (int i = cnt2; i >= 2; i--) {
                // e^(2^i) == 1
                es[i - 2] = e;
                ies[i - 2] = ie;
                e *= e;
                ie *= ie;
            }
            mint now = 1;
            for (int i = 0; i <= cnt2 - 2; i++) {
                sum_e[i] = es[i] * now;
                now *= ies[i];
            }
        }
        for (int ph = 1; ph <= h; ph++) {
            int w = 1 << (ph - 1), p = 1 << (h - ph);
            mint now = 1;
            for (int s = 0; s < w; s++) {
                int offset = s << (h - ph + 1);
                for (int i = 0; i < p; i++) {
                    auto l = a[i + offset];
                    auto r = a[i + offset + p] * now;
                    a[i + offset] = l + r;
                    a[i + offset + p] = l - r;
                }
                now *= sum_e[bsf(~(unsigned int)(s))];
            }
        }
    }
    
    template <class mint, internal::is_static_modint_t<mint>* = nullptr>
    void butterfly_inv(std::vector<mint>& a) {
        static constexpr int g = internal::primitive_root<mint::mod()>;
        int n = int(a.size());
        int h = internal::ceil_pow2(n);
    
        static bool first = true;
        static mint sum_ie[30];  // sum_ie[i] = es[0] * ... * es[i - 1] * ies[i]
        if (first) {
            first = false;
            mint es[30], ies[30];  // es[i]^(2^(2+i)) == 1
            int cnt2 = bsf(mint::mod() - 1);
            mint e = mint(g).pow((mint::mod() - 1) >> cnt2), ie = e.inv();
            for (int i = cnt2; i >= 2; i--) {
                // e^(2^i) == 1
                es[i - 2] = e;
                ies[i - 2] = ie;
                e *= e;
                ie *= ie;
            }
            mint now = 1;
            for (int i = 0; i <= cnt2 - 2; i++) {
                sum_ie[i] = ies[i] * now;
                now *= es[i];
            }
        }
    
        for (int ph = h; ph >= 1; ph--) {
            int w = 1 << (ph - 1), p = 1 << (h - ph);
            mint inow = 1;
            for (int s = 0; s < w; s++) {
                int offset = s << (h - ph + 1);
                for (int i = 0; i < p; i++) {
                    auto l = a[i + offset];
                    auto r = a[i + offset + p];
                    a[i + offset] = l + r;
                    a[i + offset + p] =
                        (unsigned long long)(mint::mod() + l.val() - r.val()) *
                        inow.val();
                }
                inow *= sum_ie[bsf(~(unsigned int)(s))];
            }
        }
    }
    
    }  // namespace internal
    
    template <class mint, internal::is_static_modint_t<mint>* = nullptr>
    std::vector<mint> convolution(std::vector<mint> a, std::vector<mint> b) {
        int n = int(a.size()), m = int(b.size());
        if (!n || !m) return {};
        if (std::min(n, m) <= 60) {
            if (n < m) {
                std::swap(n, m);
                std::swap(a, b);
            }
            std::vector<mint> ans(n + m - 1);
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    ans[i + j] += a[i] * b[j];
                }
            }
            return ans;
        }
        int z = 1 << internal::ceil_pow2(n + m - 1);
        a.resize(z);
        internal::butterfly(a);
        b.resize(z);
        internal::butterfly(b);
        for (int i = 0; i < z; i++) {
            a[i] *= b[i];
        }
        internal::butterfly_inv(a);
        a.resize(n + m - 1);
        mint iz = mint(z).inv();
        for (int i = 0; i < n + m - 1; i++) a[i] *= iz;
        return a;
    }
    
    template <unsigned int mod = 998244353,
              class T,
              std::enable_if_t<internal::is_integral<T>::value>* = nullptr>
    std::vector<T> convolution(const std::vector<T>& a, const std::vector<T>& b) {
        int n = int(a.size()), m = int(b.size());
        if (!n || !m) return {};
    
        using mint = static_modint<mod>;
        std::vector<mint> a2(n), b2(m);
        for (int i = 0; i < n; i++) {
            a2[i] = mint(a[i]);
        }
        for (int i = 0; i < m; i++) {
            b2[i] = mint(b[i]);
        }
        auto c2 = convolution(move(a2), move(b2));
        std::vector<T> c(n + m - 1);
        for (int i = 0; i < n + m - 1; i++) {
            c[i] = c2[i].val();
        }
        return c;
    }
    
    std::vector<long long> convolution_ll(const std::vector<long long>& a,
                                          const std::vector<long long>& b) {
        int n = int(a.size()), m = int(b.size());
        if (!n || !m) return {};
    
        static constexpr unsigned long long MOD1 = 754974721;  // 2^24
        static constexpr unsigned long long MOD2 = 167772161;  // 2^25
        static constexpr unsigned long long MOD3 = 469762049;  // 2^26
        static constexpr unsigned long long M2M3 = MOD2 * MOD3;
        static constexpr unsigned long long M1M3 = MOD1 * MOD3;
        static constexpr unsigned long long M1M2 = MOD1 * MOD2;
        static constexpr unsigned long long M1M2M3 = MOD1 * MOD2 * MOD3;
    
        static constexpr unsigned long long i1 =
            internal::inv_gcd(MOD2 * MOD3, MOD1).second;
        static constexpr unsigned long long i2 =
            internal::inv_gcd(MOD1 * MOD3, MOD2).second;
        static constexpr unsigned long long i3 =
            internal::inv_gcd(MOD1 * MOD2, MOD3).second;
    
        auto c1 = convolution<MOD1>(a, b);
        auto c2 = convolution<MOD2>(a, b);
        auto c3 = convolution<MOD3>(a, b);
    
        std::vector<long long> c(n + m - 1);
        for (int i = 0; i < n + m - 1; i++) {
            unsigned long long x = 0;
            x += (c1[i] * i1) % MOD1 * M2M3;
            x += (c2[i] * i2) % MOD2 * M1M3;
            x += (c3[i] * i3) % MOD3 * M1M2;
            // B = 2^63, -B <= x, r(real value) < B
            // (x, x - M, x - 2M, or x - 3M) = r (mod 2B)
            // r = c1[i] (mod MOD1)
            // focus on MOD1
            // r = x, x - M', x - 2M', x - 3M' (M' = M % 2^64) (mod 2B)
            // r = x,
            //     x - M' + (0 or 2B),
            //     x - 2M' + (0, 2B or 4B),
            //     x - 3M' + (0, 2B, 4B or 6B) (without mod!)
            // (r - x) = 0, (0)
            //           - M' + (0 or 2B), (1)
            //           -2M' + (0 or 2B or 4B), (2)
            //           -3M' + (0 or 2B or 4B or 6B) (3) (mod MOD1)
            // we checked that
            //   ((1) mod MOD1) mod 5 = 2
            //   ((2) mod MOD1) mod 5 = 3
            //   ((3) mod MOD1) mod 5 = 4
            long long diff =
                c1[i] - internal::safe_mod((long long)(x), (long long)(MOD1));
            if (diff < 0) diff += MOD1;
            static constexpr unsigned long long offset[5] = {
                0, 0, M1M2M3, 2 * M1M2M3, 3 * M1M2M3};
            x -= offset[diff % 5];
            c[i] = x;
        }
    
        return c;
    }
    
    }  // namespace atcoder
    
    using namespace atcoder;
    
    using mint=modint998244353;
    //using mint=modint1000000007;
    
    template<int mod>
    struct nCr{
        vector<static_modint<mod>> fac,inv,ifac;
        void calc(int n){
            fac.resize(n+1),inv.resize(n+1),ifac.resize(n+1);
            fac[0]=inv[1]=ifac[0]=1;
            for(int i=1; i<=n; ++i) fac[i]=fac[i-1]*static_modint<mod>::raw(i);
            for(int i=2; i<=n; ++i) inv[i]=inv[mod%i]*static_modint<mod>::raw(mod-mod/i);
            for(int i=1; i<=n; ++i) ifac[i]=ifac[i-1]*inv[i];
        }
        static_modint<mod> C(int n, int m){
            if(m<0||m>n) return 0;
            return fac[n]*ifac[m]*ifac[n-m];
        }
    };
    
    nCr<998244353> de;
    
    void ahcorz(){
        // (a_i - j)! / (a_i - i)! (a_i = a_j)
        // (a_i - j - 1)! / (a_i - i)! * (i - j) (a_i != a_j)
        int n; cin >> n;
        de.calc(n+1);
        vi a; a.pb(0);
        rep(n){
            int x; cin >> x;
            a.pb(max(a.back(),x));
        }
        vc<mint> dp(n+1),aux(n+1);
        auto solve=[&](auto &self, int l, int r) -> void{
            if(l+1==r){
                if(l==0) dp[l]=-1;
                else dp[l]+=aux[l]*l;
                dp[l]*=de.ifac[a[l]-l];
                return;
            }
            int mid=l+r>>1;
            self(self,l,mid);
            if(a[mid]==a[mid-1]){
                int x=mid-1,y=mid;
                while(x-1>=l&&a[x-1]==a[mid]) x--;
                while(y+1<r&&a[y+1]==a[mid]) y++;
                mint tot,tot1,tot2;
                rep(i,x,mid) tot+=dp[i]*de.fac[a[mid]-i],tot1+=dp[i]*de.fac[a[mid]-i-1],tot2+=dp[i]*de.fac[a[mid]-i-1]*i;
                rep(i,mid,y+1) dp[i]-=tot-(tot1*i-tot2);
            }
            vc<mint> vec;
            rep(i,a[mid]-mid,a[r-1]-(l+1)+1) vec.pb(de.fac[i]);
            vc<mint> vec1,vec2;
            rep(i,l,mid) vec1.pb(dp[i]),vec2.pb(dp[i]*i);
            auto res1=convolution(vec,vec1),res2=convolution(vec,vec2);
            int base=a[mid]-mid+l+1;
            rep(i,mid,r) if(a[i]-base>=0&&a[i]-base<sz(res1)) aux[i]-=res1[a[i]-base],dp[i]+=res2[a[i]-base];
            self(self,mid,r);
        };
        solve(solve,0,n+1);
        print(dp[n].val());
    }
    
    signed main(){
        ios_base::sync_with_stdio(0),cin.tie(0);
        cout << fixed << setprecision(20);
        int t=1;
        cin >> t;
        while(t--) ahcorz();
    }
    
    • 1

    信息

    ID
    7269
    时间
    5000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者