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;
}
posted @ 2017-09-12 21:51  permui  阅读(144)  评论(0编辑  收藏  举报