zoukankan      html  css  js  c++  java
  • Solution -「多校联训」自动机

    (mathcal{Description})

      Link.

      有一个状态集为 (V) 的自动机,状态接收 (, )_(空格) 三种字符,分别编号为 (0,1,2),状态 (u)(i) 转移指向状态 (d_{u,i}),方案数为 (e_{u,i})。求从 (s) 出发到 (t) 终止能接受的长度恰好为 (n) 的字符串中,忽略空格后正则匹配的字符串数量。模 (998244353)

      (|V|le2)(nle10^5)

    (mathcal{Solution})

      回忆一下 Catalan 数列的递推求法,我们发现暴力 DP 容易写成矩阵乘法的形式。令 (F_i=egin{bmatrix}f_{i,0,0}&f_{i,0,1}\f_{i,1,0}&f_{i,1,1}end{bmatrix}) 表示转移 (i) 步,从 (s) 走到 (t) 能形成的匹配串数量。设 (E_0,E_1,E_2) 分别表示选择 (, )_ 的转移矩阵,那么

    [F_i=sum_{j=0}^{i-2}F_jE_0F_{i-j-2}E_1+F_{i-1}E_2 ]

    发现和式是卷积形式,而矩阵系数的多项式显然适用于 FFT。所以直接 CDQ 分治(或许叫分治 FFT?)就能算出所有 (F)。实现细节比较多,复杂度 (mathcal O(|V|^2nlog^2n+|V|^3nlog n))

    (mathcal{Code})

      忙着补题,所以实现得粗糙,常数比较大 qwq。

    /*~Rainybunny~*/
    
    #include <cstdio>
    #include <cassert>
    #include <iostream>
    #include <algorithm>
    
    #define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
    #define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
    
    const int MAXN = 1e5, MAXL = 1 << 18, MOD = 998244353, MG = 3;
    
    inline int imin( const int a, const int b ) { return a < b ? a : b; }
    inline int imax( const int a, const int b ) { return a < b ? b : a; }
    inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += MOD ); }
    inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
    inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
    inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
    inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
    inline int mpow( int a, int b ) {
        int ret = 1;
        for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
        return ret;
    }
    
    struct Matrix {
        int mat[2][2];
        inline int* operator [] ( const int k ) { return mat[k]; }
        inline Matrix operator * ( const int k ) const {
            return Matrix
              { { { mul( mat[0][0], k ), mul( mat[0][1], k ) },
              { mul( mat[1][0], k ), mul( mat[1][1], k ) } } };
        }
        inline Matrix operator + ( const Matrix& t ) const {
            return Matrix
              { { { add( mat[0][0], t.mat[0][0] ), add( mat[0][1], t.mat[0][1] ) },
              { add( mat[1][0], t.mat[1][0] ), add( mat[1][1], t.mat[1][1] ) } } };
        }
        inline Matrix operator - ( const Matrix& t ) const {
            return Matrix
              { { { sub( mat[0][0], t.mat[0][0] ), sub( mat[0][1], t.mat[0][1] ) },
              { sub( mat[1][0], t.mat[1][0] ), sub( mat[1][1], t.mat[1][1] ) } } };
        }
        inline Matrix operator * ( const Matrix& t ) const {
            static Matrix ret;
            ret[0][0] = ret[0][1] = ret[1][0] = ret[1][1] = 0;
            rep ( i, 0, 1 ) rep ( k, 0, 1 ) rep ( j, 0, 1 ) {
                addeq( ret[i][j], mul( mat[i][k], t.mat[k][j] ) );
            }
            return ret;
        }
        inline void clear() { mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0; }
    };
    
    namespace PolyOper {
    
    int omega[20][MAXL + 5];
    
    inline void init() {
        rep ( i, 0, 18 ) {
            int* oi = omega[i];
            oi[0] = 1, oi[1] = mpow( MG, MOD - 1 >> i >> 1 );
            rep ( j, 2, ( 1 << i ) - 1 ) oi[j] = mul( oi[j - 1], oi[1] );
        }
    }
    
    inline void ntt( const int len, Matrix* u, const int type ) {
        static int rev[MAXL + 5];
        int tlg = 0;
        for ( ; 1 << tlg < len; ++tlg );
        rep ( i, 1, len - 1 ) rev[i] = rev[i >> 1] >> 1 | ( i & 1 ) << tlg >> 1;
        rep ( i, 0, len - 1 ) if ( i < rev[i] ) std::swap( u[i], u[rev[i]] );
    
        for ( int i = 0, stp = 1; stp < len; ++i, stp <<= 1 ) {
            int* oi = omega[i];
            for ( int j = 0; j < len; j += stp << 1 ) {
                rep ( k, j, j + stp - 1 ) {
                    Matrix ev( u[k] ), ov( u[k + stp] * oi[k - j] );
                    u[k] = ev + ov, u[k + stp] = ev - ov;
                }
            }
        }
    
        if ( !~type ) {
            int il = mpow( len, MOD - 2 );
            rep ( i, 0, len - 1 ) u[i] = u[i] * il;
            std::reverse( u + 1, u + len );
        }
    }
    
    } // namespace PolyOper.
    
    Matrix E[3], F[MAXN + 5];
    
    inline void solve( const int l, const int r ) {
        if ( l == r ) return ;
        int mid = l + r >> 1;
        
        solve( l, mid );
    
        static Matrix T[2][MAXL + 5];
        int len = 1;
        for ( ; len <= r - l + 2 << 1; len <<= 1 );
        
        rep ( i, l, mid ) T[0][i - l] = F[i] * E[0];
        rep ( i, 0, imin( l - 1, r - l - 2 ) ) T[1][i] = F[i] * E[1];
        PolyOper::ntt( len, T[0], 1 ), PolyOper::ntt( len, T[1], 1 );
        rep ( i, 0, len - 1 ) T[0][i] = T[0][i] * T[1][i], T[1][i].clear();
        PolyOper::ntt( len, T[0], -1 );
        rep ( i, imax( mid + 1, 2 ), r ) F[i] = F[i] + T[0][i - l - 2];
        rep ( i, 0, len - 1 ) T[0][i].clear();
    
        rep ( i, 0, r - l - 2 ) T[0][i] = F[i] * E[0];
        rep ( i, l, mid ) T[1][i - l] = F[i] * E[1];
        PolyOper::ntt( len, T[0], 1 ), PolyOper::ntt( len, T[1], 1 );
        rep ( i, 0, len - 1 ) T[0][i] = T[0][i] * T[1][i], T[1][i].clear();
        PolyOper::ntt( len, T[0], -1 );
        rep ( i, imax( mid + 1, 2 ), r ) F[i] = F[i] + T[0][i - l - 2];
        rep ( i, 0, len - 1 ) T[0][i].clear();
    
        F[mid + 1] = F[mid + 1] + F[mid] * E[2];
        solve( mid + 1, r );
    }
    
    int main() {
        freopen( "dfa.in", "r", stdin );
        freopen( "dfa.out", "w", stdout );
    
        int V; scanf( "%d", &V );
        rep ( i, 0, V - 1 ) {
            int d0, e0, d1, e1, d2, e2;
            scanf( "%d %d %d %d %d %d", &d0, &e0, &d1, &e1, &d2, &e2 );
            E[0][i][d0] = e0, E[1][i][d1] = e1, E[2][i][d2] = e2;
        }
    
        PolyOper::init();
        F[0][0][0] = F[0][1][1] = 1, solve( 0, MAXN );
    
        int q, s, t, n; scanf( "%d", &q );
        while ( q-- ) {
            scanf( "%d %d %d", &s, &t, &n );
            printf( "%d
    ", F[n][s][t] );
        }
        return 0;
    }
    
    
  • 相关阅读:
    Scala学习十二——高阶函数
    Scala学习十一——操作符
    Scala学习十——特质
    Scala学习九——文件和正则表达式
    Scala学习八——继承
    Scala学习七——包和引入
    Scala学习六——对象
    Scala学习五——类
    Scala学习四——映射和数组
    Spark Broadcast内幕解密:Broadcast运行机制彻底解密、Broadcast源码解析、Broadcast最佳实践
  • 原文地址:https://www.cnblogs.com/rainybunny/p/14983670.html
Copyright © 2011-2022 走看看