题解:P10106 [GDKOI2023 提高组] 马戏团里你最忙

· · 题解

马戏团里我最忙!

哈哈受某错误想法蛊惑,题解原先假了一次,原先第一步就错了,现在修订正确。

首篇做法优秀(不是瞎 BM 算)的有证明题解。

这里我们引入张量积的概念,仅仅辅助理解。

已知 A 是一个 2\times 201 矩阵。

定义张量积幂次 A^{\otimes n}_{i,j}=\prod\limits_{k=0}^n A_{i(k),j(k)},其中 x(k) 表示 x 的二进制第 k 位。

例如 \text{OR}\text{FWT} 矩阵就是 \begin{bmatrix} 1 & 0 \\ 1 & 1\end{bmatrix}^{\otimes n}

首先先写出暴力:设 f_{i,j}, g_{i,j} 分别为第 i 次操作之后为 j 的概率、与到 j 的答案,转移 FWT 一下。

具体的,考虑 (f_i,g_i)\to (f_{i+1},g_{i+1}) 是一个线性变换,我们尝试写出转移矩阵:

q=1-p,定义矩阵:

X=\begin{bmatrix} 1 & \frac{1}{2} \\[5pt] 0 & \frac{1}{2} \end{bmatrix},\quad Y=\begin{bmatrix} \frac{1}{2} & 0 \\[5pt] \frac{1}{2} & 1 \end{bmatrix} \\[10pts] A=pY^{\otimes n}+qX^{\otimes n}

相当于它达到了如下 f\gets \text{trans}(f) 的效果。

inline void trans(int *f)
{
    static int a[M],b[M];
    for(int i=0;i<m;i++) a[i]=1ll*f[i]*p%mod,b[i]=1ll*f[i]*q%mod;
    OR(a,1);AND(b,1);
    for(int i=0;i<m;i++) a[i]=(((LL)a[i])<<pc(i))%mod,b[i]=(((LL)b[i])<<(n-pc(i)))%mod;
    OR(a,0),AND(b,0);
    for(int i=0;i<m;i++) ad(f[i]=a[i],b[i]);
}// trans f=Af

下面 \cdot 是点乘,粗体表示向量,则转移就是:

\begin{cases} \mathbf{f_{i+1}}\gets A\mathbf{f_i} \\ \mathbf{g_{i+1}}\gets (A\mathbf{f_i})\cdot \mathbf{c}+A\mathbf{g_i} \end{cases}\Longrightarrow \begin{bmatrix} \mathbf{f_{i+1}}\\\mathbf{g_{i+1}} \end{bmatrix}=\mathcal{T}\begin{bmatrix} \mathbf{f_{i}}\\\mathbf{g_{i}} \end{bmatrix} \\ \\[10pt] \mathcal{T}=\begin{bmatrix} A & 0\\ B & A \end{bmatrix},B_{i,j}=c_iA_{i,j}

于是我们只需快速求出 \mathcal{T}^K。相当于我们要求出 \mathcal{T}最小多项式 m_{\mathcal{T}}(x),然后做个齐次递推。

先求 m_A(x)

由于 相似变换 不改变最小多项式,于是尝试构造 相似变换

构造 Z=\begin{bmatrix} 1 & 1 \\ 1 & -1\end{bmatrix},发现 ZX,Y 同时相似对角化了!

\begin{aligned} X'&=Z^{-1}XZ=\begin{bmatrix} 1 & 0 \\[1pt] \frac{1}{2} & \frac{1}{2} \end{bmatrix} \\[10pts] Y'&=Z^{-1}YZ=\begin{bmatrix} 1 & 0 \\[1pt] -\frac{1}{2} & \frac{1}{2} \end{bmatrix} \end{aligned}

U=X^{\otimes n},V=Y^{\otimes n},W=Z^{\otimes n},此时 W^{-1}AW=pY'^{\otimes n}+qX'^{\otimes n}=A'

此时 A' 是一个下三角矩阵,所以它的全部不重特征值就是它的对角线元素构成的集合:\{1,2^{-1},\cdots,2^{-n}\}

由此合理猜测 m_A(x)=\prod\limits_{i=0}^n (x-2^{-i})

但是这样还不够,我们还要证明最小多项式里所有的根都是一阶的,这等价于这个矩阵是 可对角化的

:::info[证明]

考虑算出 A' 矩阵,显然当 j\sube iA'_{i,j} 才有值,定义 |x|=\text{popcount}(x)

\begin{aligned} X'^{\otimes n}_{i,j}&=\prod\limits_{k=0}^n X'_{i(k),j(k)}=2^{-|i|} \\[10pts] Y'^{\otimes n}_{i,j}&=\prod\limits_{k=0}^n X'_{i(k),j(k)}=2^{-|i|}\times (-1)^{|i|-|j|} \\[10pts] A'_{i,j}&=2^{-|i|}(q+(-1)^{|i|-|j|}p) \end{aligned}

我们发现 A'_{i,j} 只和 |i|,|j| 相关。

于是我们不妨对 A' 的下标施加一个排列 p,即换基变换,此时不影响 A 是否对角化。

A' 如下分块,即把 \text{popcount} 相同的集中到一个区间内。

$$ A' = \begin{bmatrix} B_{0,0} & \mathbf{0} & \mathbf{0} & \cdots & \mathbf{0} \\ B_{1,0} & B_{1,1} & \mathbf{0} & \cdots & \mathbf{0} \\ B_{2,0} & B_{2,1} & B_{2,2} & \cdots & \mathbf{0} \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ B_{n,0} & B_{n,1} & B_{n,2} & \cdots & B_{n,n} \end{bmatrix} $$ > **定理**:设 $M$ 是一个分块三角矩阵 > $$ > M = \begin{bmatrix} > A_{1,1} & \mathbf{0} & \cdots & \mathbf{0} \\ > A_{2,1} & A_{2,2} & \cdots & \mathbf{0} \\ > \vdots & \vdots & \ddots & \vdots \\ > A_{n,1} & A_{n,2} & \cdots & A_{n,n} > \end{bmatrix} > $$ > 其中 $A_{i,i}$ 是方阵。如果对于任意 $i \neq j$,对角块 $A_{i,i}$ 和 $A_{j,j}$ 的谱(特征值集合)互不相交,即 $\text{Spectrum}(A_{i,i}) \cap \text{Spectrum}(A_{j,j}) = \varnothing$,那么: > > **$M$ 可对角化 $\iff$ 所有的对角块 $A_{ii}$ 都可对角化。** 考察我们这里的 $B_{k,k}$,发现 $B_{k,k}=2^{-k}I_{\binom{n}{k}}$,显然每块谱不交且每块可对角化。 至此我们证明了 $A'$ 可对角化。 ::: --- 求出 $m_A(x)$,如何求 $m_{\mathcal{T}}(x)$ 呢? 考虑 $m_A(\mathcal{T})=\begin{bmatrix} 0 & 0\\ B' & 0 \end{bmatrix}$,这里 $B,B'$ 并不关键。 于是 $m_A^2(\mathcal{T})=\begin{bmatrix} 0 & 0\\ 0 & 0 \end{bmatrix}$,我们得出 $m_{\mathcal{T}}(x)$ 一定是 $m_A^2(x)$ 的因子。 令 $Q(x)=m_A^2(x)=\prod\limits_{i=0}^n (x-2^{-i})^2$,而后快速幂求出 $R(x)=x^K\bmod Q(x)$。 并且 $\deg Q=2(n+1)$ 次数很小,足以快速求解。 则 $\begin{bmatrix} \mathbf{f_{K}}\\\mathbf{g_{K}} \end{bmatrix}=\sum\limits_{i=0}^{\deg Q-1} R_i\begin{bmatrix} \mathbf{f_{i}}\\\mathbf{g_{i}} \end{bmatrix}$,只需预处理出 $f,g$ 的 $0\sim 2n+1$ 项即可。 复杂度 $\mathcal{O}(n^22^n+n^2\log K)$,瓶颈在于预处理前面的项。 :::info[代码] ```cpp // 洛谷 P10106 // https://www.luogu.com.cn/problem/P10106 #include<bits/stdc++.h> #define LL long long #define pc __builtin_popcount #define fr(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout); using namespace std; const int N=77,M=1<<17|5,mod=998244353,I2=(mod+1)>>1; int n,m,p,q,K,x0,c[M],f[N][M],g[N][M]; int L,F[N],G[N],A[N],B[N]; inline int ksm(int x,int p){int s=1;for(;p;(p&1)&&(s=1ll*s*x%mod),x=1ll*x*x%mod,p>>=1);return s;} inline void ad(int &x,int y){x+=y;(x>=mod)&&(x-=mod);} inline void OR(int *a,bool o) { for(int k=1;k<m;k<<=1) for(int i=0;i<m;i+=k+k) for(int j=i;j<i+k;j++) ad(a[j+k],o?a[j]:mod-a[j]); } inline void AND(int *a,bool o) { for(int k=1;k<m;k<<=1) for(int i=0;i<m;i+=k+k) for(int j=i;j<i+k;j++) ad(a[j],o?a[j+k]:mod-a[j+k]); } inline void trans(int *f) { static int a[M],b[M]; for(int i=0;i<m;i++) a[i]=1ll*f[i]*p%mod,b[i]=1ll*f[i]*q%mod; OR(a,1);AND(b,1); for(int i=0;i<m;i++) a[i]=(((LL)a[i])<<pc(i))%mod,b[i]=(((LL)b[i])<<(n-pc(i)))%mod; OR(a,0),AND(b,0); for(int i=0;i<m;i++) ad(f[i]=a[i],b[i]); }// trans f=Af inline void mul(int *a,int *b) { static int c[N<<1]; for(int i=0;i<L;i++) for(int j=0;j<L;j++) c[i+j]=(c[i+j]+1ll*a[i]*b[j])%mod; for(int i=2*(L-1);i>=L;i--) { int x=mod-c[i];c[i]=0; for(int j=0;j<L;j++) c[i-(L-j)]=(c[i-(L-j)]+1ll*F[j]*x)%mod; } for(int i=0;i<L;i++) a[i]=c[i],c[i]=0; }// 多项式乘法取模 int main() { cin.tie(0)->sync_with_stdio(0);cin>>n>>p>>K>>x0;m=1<<n; for(int i=0;i<m;i++) cin>>c[i]; int I=ksm(m,mod-2);q=1ll*(mod+1-p)*I%mod;p=1ll*p*I%mod; for(int t=f[0][x0]=1;t<2*(n+1);t++) { for(int i=0;i<m;i++) f[t][i]=f[t-1][i],g[t][i]=g[t-1][i]; trans(f[t]),trans(g[t]); for(int i=0;i<m;i++) g[t][i]=(g[t][i]+1ll*c[i]*f[t][i])%mod; }//递推前面的项 if(K<2*(n+1)){ for(int i=0;i<m;i++) cout<<g[K][i]<<" "; return 0; } auto ml=[&](int x){ for(int i=0;i<=L;i++) G[i+1]=F[i];L++; for(int i=0;i<=L;i++) F[i]=(1ll*F[i]*x+G[i])%mod; };F[0]=1; for(int i=0,s=1;i<=n;i++,s=1ll*s*I2%mod) ml(mod-s),ml(mod-s); for(A[0]=B[1]=1;K;(K&1)&&(mul(A,B),1),mul(B,B),K>>=1); for(int i=0;i<m;i++) { int x=0; for(int j=0;j<L;j++) x=(x+1ll*A[j]*g[j][i])%mod; cout<<x<<" "; }//计算贡献 return 0; } ``` :::