zoukankan      html  css  js  c++  java
  • 「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)!} imes dp[j] ]

    这个东西是可以方便的分治FFT的,化开就是:

    [dp[i] = i!(-1)^{c_i}sum_{j+k=i} [s_j='>']-1^{c_j+1}dp[j] imes 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;
    }
    
  • 相关阅读:
    英雄联盟离线更新方法
    (七) Keras 绘制网络结构和cpu,gpu切换
    剑指offer | 链表中的倒数第K个结点 | 12
    剑指offer | 从尾到头打印链表 | 11
    剑指offer | 栈的压入,弹出序列 | 10
    剑指offer | 包含min函数的栈 | 09
    剑指offer | 调整数组顺序使奇数位于偶数前面 | 08
    剑指offer | 旋转数组的最小数字 | 07
    剑指offer | 两个栈实现一个队列 | 06
    剑指offer | 替换空格 | 05
  • 原文地址:https://www.cnblogs.com/yijan/p/12326328.html
Copyright © 2011-2022 走看看