\(\mathcal{Description}\)

  Link.

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

  \(|V|\le2\),\(n\le10^5\)。

\(\mathcal{Solution}\)

  回忆一下 Catalan 数列的递推求法,我们发现暴力 DP 容易写成矩阵乘法的形式。令 \(F_i=\begin{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|^2n\log^2n+|V|^3n\log 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\n", F[n][s][t] );
}
return 0;
}

Solution -「多校联训」自动机的更多相关文章

  1. Solution -「多校联训」排水系统

    \(\mathcal{Description}\)   Link.   在 NOIP 2020 A 的基础上,每条边赋权值 \(a_i\),随机恰好一条边断掉,第 \(i\) 条段的概率正比于 \(a ...

  2. Solution -「多校联训」I Love Random

    \(\mathcal{Description}\)   给定排列 \(\{p_n\}\),可以在其上进行若干次操作,每次选取 \([l,r]\),把其中所有元素变为原区间最小值,求能够得到的所有不同序 ...

  3. Solution -「多校联训」签到题

    \(\mathcal{Description}\)   Link.   给定二分图 \(G=(X\cup Y,E)\),求对于边的一个染色 \(f:E\rightarrow\{1,2,\dots,c\ ...

  4. Solution -「多校联训」朝鲜时蔬

    \(\mathcal{Description}\)   Link.   破案了,朝鲜时蔬 = 超现实树!(指写得像那什么一样的题面.   对于整数集 \(X\),定义其 好子集 为满足 \(Y\sub ...

  5. Solution -「多校联训」消失的运算符

    \(\mathcal{Description}\)   Link.   给定长度为 \(n\) 的合法表达式序列 \(s\),其中数字仅有一位正数,运算符仅有 - 作为占位.求将其中恰好 \(k\) ...

  6. Solution -「多校联训」假人

    \(\mathcal{Description}\)   Link.   一种物品有 长度 和 权值 两种属性,现给定 \(n\) 组物品,第 \(i\) 组有 \(k_i\) 个,分别为 \((1,a ...

  7. Solution -「多校联训」古老的序列问题

    \(\mathcal{Description}\)   Link.   给定序列 \(\{a_n\}\),和 \(q\) 次形如 \([L,R]\) 的询问,每次回答 \[\sum_{[l,r]\su ...

  8. Solution -「多校联训」Sample

    \(\mathcal{Description}\)   Link   (稍作简化:)对于变量 \(p_{1..n}\),满足 \(p_i\in[0,1],~\sum p_i=1\) 时,求 \(\ma ...

  9. Solution -「多校联训」光影交错

    \(\mathcal{Description}\)   Link.   一个游戏包含若干次卡牌抽取,每次以 \(p_l\) 的概率得到 \(+1\),\(p_d\) 的概率得到 \(-1\),否则得到 ...

随机推荐

  1. let var const 区别

    let es6 语法 let是作用域是块级的,即{}内的范围 如果未声明变量就使用的话,报错ReferenceError,而var则会报错undefined(不存在变量提升) 只要块级作用域内存在le ...

  2. nuxt 相关

    https://github.com/xuqiang521/nuxt-ssr-demo https://www.cnblogs.com/laozhang-is-phi/p/10249248.html ...

  3. SSRF服务器端请求伪造漏洞基础

    0x00 思考 1.什么是SSRF漏洞?2.SSRF漏洞的利用方式3.SSRF漏洞绕过4.SSRF漏洞加固 0x01 什么是SSRF漏洞 定义:SSRF漏洞,中文全称为服务端请求伪造漏洞,是一种由攻击 ...

  4. Vulnhub系列——持续更新

    vulnhub系列--持续更新 一 · XXE Lab: 1 ​ 使用vmware搭建靶机 ​ 扫描存活主机 ​ 找到目标主机--192.168.1.18 ​ 开始扫描目标端口 ​ 发现只开了一个80 ...

  5. Linuxqq shell脚本安装后的卸载

    官方下载和帮助页面: 传送门 linuxqq_2.0.0-b1 的时候,并没有发布 MIPS64 的 DEB 包,只能用 .sh 安装,需要手动删除卸载.愚人节发布的 beta2 新增了 MIPS64 ...

  6. 访问者模式(Visitor模式)

    模式的定义与特点 访问者(Visitor)模式的定义:将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提 ...

  7. golang中json格式化自定义日期格式

    go 的time.Time,在json序列化是默认 2006-01-02T15:04:05Z07:00 的格式,十分不便, encoding/json包在序列化和反序列化的时候分别调用encode.g ...

  8. Git安装详解

    官网地址: https://git-scm.com/ 查看 GNU 协议,可以直接点击下一步. 选择 Git 安装位置,要求是非中文并且没有空格的目录,然后下一步. Git 选项配置,推荐默认设置,然 ...

  9. APC 篇—— APC 挂入

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  10. 分治FFT小记🐤

    分治FFT:在 $O(n \log^2 n)$ 的时间内求出类似于 $f_i=\sum\limits_{j=0}^{i-1}g(i-j)f(j)$ 之类的递推式 思想:同 CDQ 分治的思想,先分成左 ...