题目
点这里看题目。
给定非负整数 \(A,B,C\),你需要求出满足下列条件的字符串 \(S\) 的个数:
-
\(S\) 中包含且仅包含恰好 \(A\) 个
A
、\(B\) 个B
、\(C\) 个C
。 -
\(S\) 中不包含子串
ABC
、BCA
或CAB
。
答案对于 \(998244353\) 取模。
所有数据满足 \(0\le A,B,C\le 10^6\)。
分析
教训:在没有尝试过常规做法之前,不要随便尝试骚的。
考虑普通的容斥。假设我们已枚举每个长度为 \(3\) 的子串是否为不合法字符串,现在计算方案数。
注意到,如果钦定的不合法子串之间有重叠,则它们之间是相容的,但是方案数会变少。具体来说,根据枚举的信息,我们可以将字符串划分成 \(k\) 段,每一段长度分别为 \(L_1,L_2,\dots,L_k\),不同段之间互不影响。则每一段的方案数仅仅由其第一个字符决定,所以总方案数为 \(3^k\)。
假如只知道 \(L_1,L_2,\dots,L_k\),怎么计算贡献?这也就是要多考虑一个容斥系数。设 \(f_n\) 为一个长度为 \(n\) 的段的容斥系数(\(n\ge 3\)),则有递推关系:
\[f_n= \begin{cases} -f_{n-1}-f{n-2}&n>3\\ -1&n=3 \end{cases} \]最后结果是:
\[f_n= \begin{cases} -1&n\equiv 0\bmod 3\\ 1&n\equiv 1\bmod 3\\ 0&n\equiv 2\bmod 3 \end{cases} \]此时我们已经得到了 \(O(m^2)\) 的 DP 做法了(设 \(m=A+B+C\))。
注意到,这个 DP 实际上就是把段按顺序组合起来(SEQ 构造),这个事 GF 也可以做。
因为三个字符数量都有限制,所以引入三元 OGF。我们将一个包含 \(a\) 个 A
、\(b\) 个 B
、\(c\) 个 C
的字符串映射到 \(x^ay^bz^c\) 上。
设 \(F(x,y,z)\) 为单独一段的 OGF,而 \(G(x,y,z)\) 为答案的 OGF。根据组合对象可以直接得到:
\[\begin{aligned} F(x,y,z)&=\frac{-3xyz}{1-xyz}+\frac{x+y+z}{1-xyz}\\ G(x,y,z)&=\frac{1}{1-F(x,y,z)}\\ &=\frac{1}{1-\frac{x+y+z-3xyz}{1-xyz}}\\ &=\frac{1-xyz}{1-(x+y+z)+2xyz} \end{aligned} \]Note.
按照一段的长度模 \(3\) 的余数进行分类。\(F\) 的第一项表示 \(n\bmod 3=0\) 的段,\(F\) 的第二项表示 \(n\bmod 3=1\) 的段。
怎么计算答案?换句话说,给定 \(a,b,c\),怎么计算 \(h(a,b,c)=[x^ay^bz^c]\frac{1}{1-(x+y+z)+2xyz}\)?我们直接展开:
\[\begin{aligned} h(a,b,c) &=[x^ay^bz^c]\sum_{n\ge 0}[(x+y+z)-2xyz]^n\\ &=[x^ay^bz^c]\sum_{n\ge 0}\sum_{k=0}^n\binom{n}{k}(-2xyz)^k(x+y+z)^{n-k}\\ &=[x^ay^bz^c]\sum_{k\ge 0}(-2)^kx^ky^kz^k\sum_{n\ge k}\binom{n}{k}(x+y+z)^{n-k}\\ &=\sum_{0\le k\le \min\{a,b,c\}}(-2)^k[x^{a-k}y^{b-k}z^{c-k}]\sum_{n\ge 0}\binom{n+k}{k}(x+y+z)^n\\ &=\sum_{0\le k\le \min\{a,b,c\}}(-2)^k\binom{a+b+c-2k}{k}\binom{a+b+c-3k}{a-k,b-k,c-k} \end{aligned} \]最后一步的解释:注意到 \((x+y+z)^n\) 展开的所有项都是 \(n\) 次的,所以包含 \(x^{a-k}y^{b-k}z^{c-k}\) 的项对应的 \(n\) 必然为 \(a+b+c-3k\)。另,\(\dbinom{a+b+c-3k}{a-k,b-k,c-k}\) 为多项式系数。
于是,这个计算可以在 \(O(A+B+C)\) 的时间内完成。
代码
#include <cstdio>
#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
const int mod = 998244353;
const int MAXN = 6e6 + 5;
template<typename _T>
inline void Read( _T &x ) {
x = 0; char s = getchar(); bool f = false;
while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
if( f ) x = -x;
}
template<typename _T>
inline void Write( _T x ) {
if( x < 0 ) putchar( '-' ), x = -x;
if( 9 < x ) Write( x / 10 );
putchar( x % 10 + '0' );
}
int fac[MAXN], ifac[MAXN];
int A, B, C;
inline int Qkpow( int, int );
inline int Inv( const int &a ) { return Qkpow( a, mod - 2 ); }
inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }
inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; }
inline int Binom( int n, int m ) { return n < m ? 0 : Mul( fac[n], Mul( ifac[m], ifac[n - m] ) ); }
inline int Qkpow( int base, int indx ) {
int ret = 1;
while( indx ) {
if( indx & 1 ) MulEq( ret, base );
MulEq( base, base ), indx >>= 1;
}
return ret;
}
inline void Init( const int &n ) {
fac[0] = 1; rep( i, 1, n ) fac[i] = Mul( fac[i - 1], i );
ifac[n] = Inv( fac[n] ); per( i, n - 1, 0 ) ifac[i] = Mul( ifac[i + 1], i + 1 );
}
inline int Query( const int &a, const int &b, const int &c ) {
if( a < 0 || b < 0 || c < 0 ) return 0;
int ret = 0, pw = 1;
for( int k = 0 ; k <= a && k <= b && k <= c ; k ++ ) {
AddEq( ret, Mul( Mul( pw, Binom( a + b + c - 2 * k, k ) ),
Mul( fac[a + b + c - 3 * k], Mul( Mul( ifac[a - k], ifac[b - k] ), ifac[c - k] ) ) ) );
MulEq( pw, mod - 2 );
}
return ret;
}
int main() {
Read( A ), Read( B ), Read( C ), Init( 2 * ( A + B + C ) );
Write( Sub( Query( A, B, C ), Query( A - 1, B - 1, C - 1 ) ) ), putchar( '\n' );
return 0;
}
失败的脑洞
自动机
判断一个字符串是否合法可以用自动机。
很容易想到一个有 \(9\) 个结点的自动机(存储最后的两个字符)。进一步可以考虑将 AA
和 BA
合并、BB
和 CB
合并、CC
和 AC
合并,于是就只有 \(6\) 个结点了。最后注意到 BA
、CB
、AC
本质上代表了两条长度为 \(2\) 的边,把字符串按照长度奇偶性分类后,每次转移两个字符,就可以压缩到 \(3\) 个结点。
关键在于,自动机仅仅关注了局部的限制,而全局字符数量限制是自动机本身无法解决的。如果想要满足这一点,我们可能只能在转移边中引入形式变元,但这会导致幂次变得非常难看。
唯一的想法是:设转移矩阵为 \(M(x,y,z)\),考虑 \(\sum_{n\ge 0}M(x,y,z)^n=\frac{I}{I-M(x,y,z)}\),这样可以将问题变成求逆。三阶求个逆应该还是没有问题吧?
不过我猜测将矩阵转成对角矩阵算幂和求逆本质上应该是类似的?
对称性
既然字符数量限制是全局的,那能不能用魔法打败魔法,用全局操作迎合全局限制?
具体地,在字符上作用 \(S_3\) 的变换,然后观察合法性的变化。结果是,有些不合法字符串是这种对称变换无法区分的。
周期性
这个就不是脑洞了。
我们可以很快地写出容斥系数 \(\{f_n\}\) 的 OGF \(F(x)\):
\[F(x)=\frac{-x^3}{1+x+x^2} \]本来尝试的是对于分母直接分解,结果解出来两个复根 \(\frac{-1-\sqrt 3i}{2},\frac{-1+\sqrt 3i}{2}\)。定睛一看,居然还是 \(e^{\frac{2\pi}{3}i}\) 和 \(e^{\frac{4\pi}{3}i}\),两个单位根。
马上注意到 \(F(x)=\frac{-x^3(1-x)}{1-x^3}\)。这在某种意义上解释了周期性。
标签:ABC,frac,int,return,Another,inline,Problem,mod,const From: https://www.cnblogs.com/crashed/p/17077277.html