题解:P16244 【MX-X27-T5】重叠

· · 题解

题意简述

给定偶数长度 n。对每个有序合法括号序列对,保留两串对应位置相同的字符。统计提取结果的层数分别为 1\sim\frac{n}{2} 时的序列对数量。

解题思路

把左、右括号分别记为 1,-1。设两条原序列在读完前 i 个字符后的前缀和为 x_i,y_i。若当前位置字符相同,两个前缀和的增量之和为 2-2。提取序列相应增加左括号或右括号。若字符不同,增量之和为 0,也不会提取字符。因此,扫描到原串位置 i 时,提取序列的前缀和为:

\frac{x_i+y_i}{2}

由于 x_i,y_i 始终非负且最终均为 0,提取结果一定是合法括号序列。它的层数就是上述前缀和的最大值。

记层数不超过 d 的序列对数量为 F_d。最终需要输出 F_d-F_{d-1}。两个合法括号序列的首字符都是左括号,所以提取结果非空,即 F_0=0

接下来计算 F_d。将两条前缀和路径各向上平移一格,令 p_i=x_i+1q_i=y_i+1,并令 w=2d+4r_i=w-q_i。原序列合法等价于 p_i>0r_i<w。层数限制可以改写为:

p_i+q_i\le2d+2\iff p_i\le r_i-2 设 $P(u,v)$ 表示在 $(0,w)$ 内从高度 $u$ 走到 $v$ 的路径数。由非交路径行列式,结合上下对称性可得: $$ F_d=P(1,1)^2-P(1,w-1)^2 $$ 这里的减项也可以理解为交换终点的路径对。交换终点的两条路径必然相交。在第一次交点交换后缀,就与同终点路径对中的相交方案一一对应。 令 $m=\frac{n}{2}$、$h=d+2$,则 $w=2h$。对单条路径反复反射越过的上下边界,终点镜像会相隔 $2h$。从高度 $1$ 回到 $1$ 时,正负镜像对应的向上步数相差一;走到 $w-1$ 时,对应偏移为奇数倍的 $h$。因此: $$ \begin{aligned} P(1,1) & =\sum_{j\in\mathbb{Z}}\left(\binom{n}{m+2jh}-\binom{n}{m+2jh-1}\right) \\ P(1,w-1) & =\sum_{j\in\mathbb{Z}}\left(\binom{n}{m+(2j+1)h-1}-\binom{n}{m+(2j+1)h}\right) \end{aligned} $$ 为了只枚举非负下标,定义: $$ b_t=\binom{n}{m+t}-\binom{n}{m+t-1} $$ 由组合数的对称性 $inom{n}{m-t}=\binom{n}{m+t}$,有 $b_{-t}=-b_{t+1}$。把正负偏移配对后,上面两项分别化为: $$ \begin{aligned} P(1,1) & =b_0+\sum_{j\ge1}(b_{2jh}-b_{2jh+1}) \\ P(1,w-1) & =\sum_{j\ge0}(b_{(2j+1)h+1}-b_{(2j+1)h}) \end{aligned} $$ 当下标超过 $m+1$ 后,各项均为 $0$。预处理阶乘、逆阶乘和所有 $b_t$,再对每个 $h$ 枚举它的倍数即可。总枚举次数是调和级数,时间复杂度为 $O(n\log n)$,空间复杂度为 $O(n)$。 ## 参考代码 ```cpp #include <bits/stdc++.h> using namespace std; using ll=long long; const int N=407697; const int mod=998244353; int n; int fac[N],jv[N],g[N]; ll Pow(ll x,ll y) { x%=mod; ll res=1; while(y) { if(y&1)res=res*x%mod; x=x*x%mod; y>>=1; } return res; } int C(int n,int m) { if(m<0||m>n)return 0; return (ll)fac[n]*jv[m]%mod*jv[n-m]%mod; } void init() { fac[0]=1; for(int i=1;i<=n;i++)fac[i]=(ll)fac[i-1]*i%mod; jv[n]=Pow(fac[n],mod-2); for(int i=n;i;i--)jv[i-1]=(ll)jv[i]*i%mod; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n; init(); int m=n/2; for(int i=0;i<=m+2;i++)g[i]=(C(n,m+i)-C(n,m+i-1)+mod)%mod; int lst=0; for(int i=1;i<=m;i++) { int h=i+2; int x=g[0],y=0; for(int j=2*h;j<=m+1;j+=2*h)x=(x+(ll)g[j]-g[j+1]+mod)%mod; for(int j=h;j<=m+1;j+=2*h)y=(y+(ll)g[j+1]-g[j]+mod)%mod; int cur=((ll)x*x%mod-(ll)y*y%mod+mod)%mod; cout<<(cur-lst+mod)%mod<<(i==m?'\n':' '); lst=cur; } return 0; } ```