「LibreOJ NOI Round #2」不等关系

「LibreOJ NOI Round #2」不等关系

暑假 dls 讲过但是当时掉线了。。

考虑我们如果直接无视 > 符号,现在这个排列就一定是一些下降的子段组合在一起的。我们假设这些段的长度分别是 $ a_1,a_2,\dots ,a_k $ 那么算出来方案数量是
\[ \frac{n!}{a_1!a_2!\dots a_k!} \]
因为我们可以任意定一个排列,为了满足条件只需要把这些子段排个序。理解一下发现就是这个东西。

但是我们还有 > 符号啊,既然不好处理,我们可以考虑容斥,比如, <<><> 这个序列,它的答案就可以推出来

<<><> = <<*<* - <<<<* - <<*<< + <<<<<

为啥是对的?我们可以把每个位置拖出来,发现 这个位置是 > = 这个位置关系任意 - 这个位置是 >

所以我们可以考虑用 dp 做这个容斥, $ dp[i] $ 表示前 $ i $ 个关系被满足的方案数量,则有
\[ dp[i] = i![s_i='>']\sum_j [s_j='>'](-1)^{cnt_i-cnt_j-1}\frac{1}{(i-j)!} \times dp[j] \]
这个东西是可以方便的分治FFT的,化开就是:
\[ dp[i] = i!(-1)^{c_i}\sum_{j+k=i} [s_j='>']-1^{c_j+1}dp[j] \times \frac{1}{k!} \]
写的时候不知道哪里系数犯了,算出来是负的。。。

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
using namespace std;
#define MAXN 400006
int n , m;
#define P 998244353
typedef long long ll;

namespace cdqntt {
    int Pow(int x, int y) {
        int res = 1;
        while (y) {
            if (y & 1) res = res * (ll) x % P;
            x = x * (ll) x % P, y >>= 1;
        }
        return res;
    }

    int wn[2][MAXN];

    void getwn(int l) {
        for (int i = 1; i < (1 << l); i <<= 1) {
            int w0 = Pow(3, (P - 1) / (i << 1)), w1 = Pow(3, P - 1 - (P - 1) / (i << 1));
            wn[0][i] = wn[1][i] = 1;
            for (int j = 1; j < i; ++j)
                wn[0][i + j] = wn[0][i + j - 1] * (ll) w0 % P,
                        wn[1][i + j] = wn[1][i + j - 1] * (ll) w1 % P;
        }
    }

    int rev[MAXN];

    void getr(int l) { for (int i = 1; i < (1 << l); ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << l - 1); }

    void NTT(int *A, int len, int f) {
        for (int i = 0; i < len; ++i) if (rev[i] < i) swap(A[i], A[rev[i]]);
        for (int l = 1; l < len; l <<= 1)
            for (int i = 0; i < len; i += (l << 1))
                for (int k = 0; k < l; ++k) {
                    int t1 = A[i + k], t2 = A[i + l + k] * (ll) wn[f][l + k] % P;
                    A[i + k] = (t1 + t2) % P;
                    A[i + l + k] = (t1 - t2 + P) % P;
                }
        if (f == 1) for (int inv = Pow(len, P - 2), i = 0; i < len; ++i) A[i] = A[i] * (ll) inv % P;
    }

    int f[MAXN];
    int A[MAXN], B[MAXN] , J[MAXN] , ok[MAXN] , t[MAXN];

    void CDQ(int *a, int l, int r) {
//        cout << l << ' ' << r << endl;
        if (l == r) return;
        int m = l + r >> 1;
        CDQ(a, l, m);
        int p = 1, len = 0;
        while (p <= (r - l + 1) * 2) p <<= 1, ++len;
        getr(len);
        for (int i = 0; i < p; ++i) A[i] = B[i] = 0;
        for (int i = l; i <= m; ++i) if( ok[i] ) B[i - l] = ( 1ll * ( ( t[i] & 1 ) ? P-1 : 1 ) * f[i] ) % P;//, cout << B[i - l] <<' ';
        for (int i = 0; i <= r - l; ++i) A[i] = J[i];// , cout << A[i] << ' ';
        puts("");
        NTT(A, p, 0), NTT(B, p, 0);
        for (int i = 0; i < p; ++i) A[i] = 1ll * A[i] * B[i] % P;
        NTT(A, p, 1);
        for (int i = m + 1; i <= r; ++i) f[i] = (f[i] + 1ll * ( ( t[i] & 1 ) ? 1 : P-1 ) * A[i - l]) % P;
        CDQ(a, m + 1, r);
    }
}

int A[MAXN];
char s[MAXN];

int main() {
//    freopen("in.in","r",stdin);
    using namespace cdqntt;
    J[0] = 1;for( int i = 1 ; i < MAXN ; ++ i ) J[i] = 1ll * J[i - 1] * Pow( i , P - 2 ) % P;
    scanf("%s",s + 1);
    n = strlen( s + 1 ) + 1;
    int pre = 0;
    for( int i = 1 ; i <= n ; ++ i ) {
        if( s[i] == '>' ) ok[i] = 1;
        t[i] = t[i - 1] + ok[i];
    }
    int l = 0 , len = 1;
    while( len <= n + n ) len <<= 1 , ++ l;
    getwn( l );
    f[0] = 1 , ok[0] = 1;
    CDQ( f , 0 , n );
    cout << ( P - 1ll * f[n] * Pow( J[n] , P - 2 ) % P ) << endl;
}

你可能感兴趣的:(「LibreOJ NOI Round #2」不等关系)