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;
}

转载于:https://www.cnblogs.com/owenyu/p/7512405.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值