题解:P15495 [ICPC 2025 APC] Tower of Hanoi

· · 题解

线段树维护动态 DP。不错的练手题咋没人做。

不太需要脑子的矩阵写法。

考虑 \Theta(nq) 做法。修改直接改,对于静态问题,我们有如下的 DP。假设序列 a_i\in\{1,2,3\},计算三柱汉诺塔最小步数。

设 dp_{i,j} 表示前 i 个盘从初始状态到全部摞在第 j 个柱子上的状态所需的最小步数。 我们考虑第 i 个也就是最大的盘,如果它已经在目标柱 j,则直接继承 dp_{i-1,j} 即可,否则需要三步:

  1. 先把前 i-1 个盘全部放到“除了 a_i 和 j 之外的第三个柱”(即第 6-a_i-j 个柱)上。
  2. 然后把第 i 个盘放到目标柱 j。
  3. 最后把这个 6-a_i-j 号柱上的塔挪到第 j 个柱上。

形式化地,我们有:

dp_{i,j}= \begin{cases} dp_{i-1,j}&a_i=j,\\ dp_{i-1,6-j-a_i}+1+f_{i-1}\quad &\text{otherwise}. \end{cases}

其中 f_k 表三柱汉诺塔中把 k 个盘的塔完整挪到另一个塔上的最小步数(初始其他两柱皆为空),由上文类似的推导过程有 f_k=2f_{k-1}+1,解得 f_k=2^k-1。于是当 a_i\ne j 时,dp_{i,j}=dp_{i-1,6-j-a_i}+1+f_{i-1}=dp_{i-1,6-j-a_i}+2^{i-1}。

静态就做完了。发现状态很小,用矩阵描述这个转移。三个 a_i 对应三个不同的转移矩阵,以 a_i=1 为例:

\begin{bmatrix} 1&0&0&0\\ 0&0&1&1\\ 0&1&0&1\\ 0&0&0&2 \end{bmatrix} \times \begin{bmatrix} dp_{i-1,1}\\ dp_{i-1,2}\\ dp_{i-1,3}\\ 2^{i-1} \end{bmatrix} = \begin{bmatrix} dp_{i,1}\\ dp_{i,2}\\ dp_{i,3}\\ 2^i \end{bmatrix} ::::success[AC Code] 常数较大。 三个转移矩阵可以用好看一点的方法预处理的,但是我懒得改。 ```cpp line-numbers #include<iostream> #include<algorithm> #include<vector> #include<cassert> #define int long long using namespace std; const int N=1e5+7,MOD=998244353; int a[N]; struct matrix{ int n,m; int a[5][5]; friend matrix operator*(matrix a,matrix b){ matrix c={a.n,b.m,{0}}; if(a.m!=b.n) return c; for(int i=1;i<=c.n;i++) for(int k=1;k<=a.m;k++) for(int j=1;j<=c.m;j++) (c.a[i][j]+=a.a[i][k]*b.a[k][j])%=MOD; return c; } }I={4,4,{{}, {0,1,0,0,0}, {0,0,1,0,0}, {0,0,0,1,0}, {0,0,0,0,1} }},A={4,1,{ {},{},{},{}, {0,1},}}, M[4]={ {0,0,{}}, {4,4,{{}, {0,1,0,0,0}, {0,0,0,1,1}, {0,0,1,0,1}, {0,0,0,0,2}}}, {4,4,{{}, {0,0,0,1,1}, {0,0,1,0,0}, {0,1,0,0,1}, {0,0,0,0,2}}}, {4,4,{{}, {0,0,1,0,1}, {0,1,0,0,1}, {0,0,0,1,0}, {0,0,0,0,2}}} }; struct node{ int l,r; matrix mtrx; }tr[N<<2]; void build(int u,int l,int r){ tr[u]={l,r,M[a[l]]}; if(l==r) return ; int mid=(l+r)>>1; build(u<<1,l,mid),build(u<<1|1,mid+1,r); tr[u].mtrx=tr[u<<1|1].mtrx*tr[u<<1].mtrx; } void upd(int u,int x,int k){ if(tr[u].l>x || tr[u].r<x) return ; if(tr[u].l==tr[u].r){ tr[u].mtrx=M[k]; return ; } upd(u<<1,x,k),upd(u<<1|1,x,k); tr[u].mtrx=tr[u<<1|1].mtrx*tr[u<<1].mtrx; } matrix ask(int u,int l,int r){ if(tr[u].l>r || tr[u].r<l) return I; if(tr[u].l>=l && tr[u].r<=r) return tr[u].mtrx; return ask(u<<1|1,l,r)*ask(u<<1,l,r); } signed main(){ ios::sync_with_stdio(0); cin.tie(0); int n,q; cin>>n>>q; for(int i=1;i<=n;i++){ cin>>a[i]; } build(1,1,n); while(q--){ char op; int l,r,x,y; cin>>op; if(op=='c'){ cin>>x>>y; upd(1,x,y); } if(op=='s'){ cin>>l>>r; matrix ans=ask(1,l,r)*A; cout<<ans.a[1][1]<<'\n'; } } return 0; } ``` ::::