题解:P10106 [GDKOI2023 提高组] 马戏团里你最忙
masterhuang
·
·
题解
马戏团里我最忙!
哈哈受某错误想法蛊惑,题解原先假了一次,原先第一步就错了,现在修订正确。
首篇做法优秀(不是瞎 BM 算)的有证明题解。
这里我们引入张量积的概念,仅仅辅助理解。
已知 A 是一个 2\times 2 的 01 矩阵。
定义张量积幂次 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},发现 Z 把 X,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 i 时 A'_{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;
}
```
:::