ZOJ3256-Tour in the Castle
题意
有一个 \(n\times m\) 的棋盘,要从 \((1,1)\) 走到 \((n,1)\) ,经过所有格子一次且仅一次,求方案数。\(2\le n\le 7,1\le m\le 10^9\) 。
分析
这是一个曼哈顿路径问题,做法应该基于插头dp,但 \(m\) 非常大,考虑是否有优化的方法。
从一列的某个状态转移到下一列的某个状态,所有转移都是一样的!
于是枚举每一个行状态,求出它转移到下一行的方案数,用矩阵乘法处理到前 \(m-1\) 列,最后一列再拿出来dp一下即可。
复杂度为 \(O(ns^2+s^3\log m)\) 。
代码
直接用所有状态去矩阵乘法是会TLE的,不过可以去掉开头不是 0 的状态,剩下的大概只有 100 种左右,就能过了。
#include<bits/stdc++.h>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long giant;
typedef cc_hash_table<int,int> Map;
typedef Map::iterator itt;
const int maxm=9;
const int maxs=350; // enough?
const int numb=2.7e5;
const int q=7777777;
inline int Plus(int x,int y) {return ((giant)x+(giant)y)%q;}
inline void Pe(int &x,int y) {x=Plus(x,y);}
inline int Sub(int x,int y) {return Plus(x,q-y);}
inline int Multi(int x,int y) {return (giant)x*y%q;}
int n,m,id[numb],mat[numb][maxm],ids,c[maxm],back[maxs],all,aid[numb];
Map f,g;
struct Matrix {
int a[maxs][maxs];
Matrix () {clear();}
inline void clear() {memset(a,0,sizeof a);}
inline void eye() {clear();for (int i=1;i<=all;++i) a[i][i]=1;}
inline int* operator [] (int x) {return a[x];}
friend Matrix operator * (Matrix a,Matrix b) {
Matrix ret;
for (int k=1;k<=all;++k) for (int i=1;i<=all;++i) for (int j=1;j<=all;++j) Pe(ret.a[i][j],Multi(a.a[i][k],b.a[k][j]));
return ret;
}
} A,B;
inline int conv(int x) {return x?(x==1?1:-1):0;}
inline void match(int mt[]) {
static int sta[maxm];
int top=0;
for (int i=1;i<=m+1;++i) if (c[i]==1) sta[++top]=i; else if (c[i]==2) {
int x=sta[top--];
mt[x]=i,mt[i]=x;
}
}
inline int gen() {
int ret=0;
for (int i=m+1;i;--i) (ret+=c[i])<<=2;
return ret;
}
void dfs(int now,int sum) {
if (now>m+1) {
if (sum) return;
int s=gen();
match(mat[id[s]=++ids]);
back[ids]=s;
return;
}
for (int i=0;i<3;++i) if (sum+conv(i)>=0) c[now]=i,dfs(now+1,sum+conv(i));
}
inline int get(int x,int p) {return (x>>(p<<1))&3;}
inline int mod(int x,int p,int d) {return (x&(~(3<<(p<<1))))+(d<<(p<<1));}
void work() {
if (!((~m&1) || (n&1))) {
puts("Impossible");
return;
}
if (n==1) {
puts("1");
return;
}
memset(id,0,sizeof id),memset(aid,0,sizeof aid),memset(mat,0,sizeof mat),ids=0,all=0;
dfs(1,0);
B.clear(),A.clear();
for (int i=1;i<=ids;++i) if (get(back[i],1)==0) aid[back[i]]=++all;
for (int i=1;i<=ids;++i) if (get(back[i],1)==0) {
f.clear(),g.clear();
f[back[i]]=1;
for (int j=1;j<=m;++j) {
f.swap(g),f.clear();
for (itt it=g.begin();it!=g.end();++it) {
const int &d=it->first,&w=it->second;
int x=get(d,j),y=get(d,j+1),*mt=mat[id[d]];
if (x==0 && y==0) Pe(f[mod(mod(d,j,1),j+1,2)],w); else
if (x==1 && y==1) {
int v=mod(mod(d,j,0),j+1,0);
v=mod(v,mt[j+1],1);
Pe(f[v],w);
} else if (x==2 && y==2) {
int v=mod(mod(d,j,0),j+1,0);
v=mod(v,mt[j],2);
Pe(f[v],w);
} else if (x==0 || y==0) {
Pe(f[mod(mod(d,j,x+y),j+1,0)],w);
Pe(f[mod(mod(d,j,0),j+1,x+y)],w);
} else if (x==2 && y==1) Pe(f[mod(mod(d,j,0),j+1,0)],w);
}
}
for (itt it=f.begin();it!=f.end();++it) {
const int &d=it->first,&w=it->second;
if (get(d,m+1)==0) A[aid[back[i]]][aid[d<<2]]=w;
}
}
B.eye();
for (int i=n-1;i;i>>=1,A=A*A) if (i&1) B=B*A;
int fir=aid[mod(mod(0,2,1),m+1,2)];
f.clear(),g.clear();
for (int i=1;i<=all;++i) if (B[fir][i]) f[back[i]]=B[fir][i];
for (int j=1;j<=m;++j) {
f.swap(g),f.clear();
for (itt it=g.begin();it!=g.end();++it) {
const int &d=it->first,&w=it->second;
int x=get(d,j),y=get(d,j+1),*mt=mat[id[d]];
if (x==0 && y==0) Pe(f[mod(mod(d,j,1),j+1,2)],w); else
if (x==1 && y==1) {
int v=mod(mod(d,j,0),j+1,0);
v=mod(v,mt[j+1],1);
Pe(f[v],w);
} else if (x==2 && y==2) {
int v=mod(mod(d,j,0),j+1,0);
v=mod(v,mt[j],2);
Pe(f[v],w);
} else if (x==0 || y==0) {
Pe(f[mod(mod(d,j,x+y),j+1,0)],w);
Pe(f[mod(mod(d,j,0),j+1,x+y)],w);
} else if (x==1 && y==2) {
if (j==m) Pe(f[mod(mod(d,j,0),j+1,0)],w);
} else if (x==2 && y==1) Pe(f[mod(mod(d,j,0),j+1,0)],w);
}
}
printf("%d\n",f[0]);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
while (~scanf("%d%d",&m,&n)) work();
return 0;
}