2025.10.27C 城堡考古 题解
有同学让我造福人类,所以来写一篇。
考虑显然没有什么通项公式可以利用的,但是注意到 \(m\) 仅仅只有小小的 \(6\),考虑状压 \(dp\) 的思路。设 \(dp_{i,j}\) 表示当前已经排了 \(i\) 列,状态为 \(j\) 的方案数,其中 \(1\) 表示该位置是一个跨了 \(i,i+1\) 两行的砖头,反之为 \(0\)。再设 \(g_{i,j}\) 表示 \(j\) 状态能否成为 \(i\) 状态的后继状态,则有:
考虑 \(g_{s,t}\) 何时为 \(0\):
- 当 \(s\wedge t\ne 0\) 时,出现重叠,为 \(0\);
- 当有一个极长的满足连续每个位置都有 \(((s\vee t)>>i)\wedge 1=0\) 的连续段满足长度为奇数时,出现重叠,为 \(0\)。
其余情况为 \(1\)。显然这个公式可以进行矩阵快速幂,将时间优化到 \(O((2^m+1)^3len)\)。加一是因为矩阵里要留一维存 \(s=0\) 的前缀和。
考虑这样肯定是会 \(T\) 的,但是我们可以发现,像 \(010101\) 这样的状态肯定是不会被便利到的,所以只需要统计所有能被遍历到的状态即可。看起来很少,但实际上可以把状态数从 \(2^m\le 64\) 变成 \(\dbinom{m}{\lfloor\frac m2\rfloor}\le 20\),时间复杂度暴减。
最终时间复杂度 \(O(\dbinom{m}{\lfloor\frac m2\rfloor}^3len)\)。可以通过本题。
关于 \(\dbinom{m}{\lfloor\frac m2\rfloor}\) 就是能被遍历到的所有状态数量的证明:
考虑黑白染色,那么黑白格数量相等就是能够被 \(1\times 2\) 或 \(2\times 1\) 的砖头密铺的充要条件。
我们设当前我们在考虑第 \(i\) 列。设状态 \(s\) 的奇数位中 \(1\) 的数量为 \(x\),偶数位中 \(1\) 的数量为 \(y\),则当且仅当 \(x=y\) 成立时,这一列的状态可以为 \(s\)。特别说明,当 \(i,m\) 为奇数时,本身就会多出一个格子,所以此时我们追求的状态也要多出一个格子。
显然,满足这个式子的状态数实际上就等价于 \(\sum\limits_{i=0}^{\lfloor\frac m2\rfloor}\dbinom{\lfloor\frac m2\rfloor}{i}\dbinom{\lceil\frac m2\rceil}i\)。通过范德蒙德恒等式,可证明 \(\dbinom{m}{\lfloor\frac{m}{2}\rfloor}=\sum\limits_{i=0}^{\lfloor\frac m2\rfloor}\dbinom{\lfloor\frac m2\rfloor}{i}\dbinom{\lceil\frac m2\rceil}i\)。
Q.E.D.
#include<bits/stdc++.h>
using namespace std;
const int p=998244353;
int m,ans,a[3005],g[64][64],vs[64],num[21],mx;
struct matrix{int z[21][21];}Z,zy,C;string s,t;
inline matrix operator*(matrix x,matrix y){
matrix z;memset(z.z,0,sizeof(z.z));
for(int i=0;i<=mx;i++) for(int j=0;j<=mx;j++)
for(int k=0;k<=mx;k++) z.z[i][k]=(z.z[i][k]+1ll*x.z[i][j]*y.z[j][k])%p;
return z;
}inline void dfs(int x){
vs[x]=1;for(int i=0;i<(1<<m);i++)
if(!vs[i]&&g[x][i]) dfs(i);
}inline int numc(){
zy=Z;for(int i=0;i<=mx;i++)
for(int j=0;j<=mx;j++) C.z[i][j]=(!i&&!j);
while(1){
if(a[0]%2==1) C=C*zy;
zy=zy*zy;int flag=1;
for(int i=3000,ls=0,cc;~i;i--)
cc=a[i]%2,a[i]=(a[i]+ls*10)/2,flag&=(!a[i]),ls=cc;
if(flag) break;
}return (C.z[0][0]+C.z[0][mx])%p;
}signed main(){
freopen("decoration.in","r",stdin);
freopen("decoration.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0),cin>>s>>t>>m;
for(int i=0;i<(1<<m);i++) for(int j=0,sm=0;j<(1<<m);j++,sm=0){
if(i&j) continue;int fl=0;
for(int k=0;k<m;k++){
int cc=((i|j)>>k)&1;
(!cc?sm++:(fl|=sm%2,sm=0));
}if(!fl&&sm%2==0) g[i][j]=1;
}dfs(0);for(int i=0;i<(1<<m);i++) if(vs[i]) num[mx++]=i;
Z.z[mx][mx]=Z.z[0][mx]=1;
for(int i=0;i<mx;i++)
for(int j=0;j<mx;j++)
Z.z[i][j]=g[num[i]][num[j]];
for(int i=0;s[i];i++) a[s.size()-i-1]=s[i]-'0';
a[0]--;for(int i=0;a[i]<0;i++)
a[i]+=10,a[i+1]--;ans-=numc();
for(int i=0;t[i];i++) a[t.size()-i-1]=t[i]-'0';
cout<<(ans+numc()+p)%p;
return 0;
}

浙公网安备 33010602011771号