ZOJ 3256 Tour in the Castle(插头DP+矩阵快速幂)

34 篇文章 1 订阅

Tour in the Castle

Time Limit: 5 Seconds       Memory Limit: 32768 KB

After the final BOSS is defeated, the hero found that the whole castle is collapsing (very familiar scene, isn't it). Escape from the castle is easy, just need to cross a few rooms. But as the Hero is full of adventurous spirit, he decides to visit every room before he escape the castle.

The castle is a rectangle with N * M rooms in it. Two rooms are connected if they share a common edge. The hero starts in the top left room. And the bottom left room is the only way out. After the hero visits a room and leaves it, it will collapse immediately(Another familiar scene). So he can visit each room only once.

The diagram shows one tour over a castle with 4 * 10 rooms:

Input

There are multiply cases (<20), process to the end of file.

Each case contains a line with two Integer N and M (2 <= N <= 7, 1 <= M <=10^9).

Ouput

For each case, if it's impossible to visit every room exactly once and get to the bottom left room, output "Impossible". Otherwise, output the number of tours as it describe above. Beacause the answer can be huge, you just need to output the answer MOD 7777777.

Sample Input

3 2
3 3
4 10

Sample Output

Impossible
2
2329

题目大意:

    给你一个N(2<=N<=7)*M(1<=M<=1e9)的矩阵,问从左上角到左下角每个点进过一次的路径数。


解题思路:

    题意就是一个普通的插头dp,但是M达到了1e9,所以我们需要使用快速幂优化。要想使用快速幂我们需要处理出列与列之间不同状态的转移情况,预处理出转移矩阵,每次直接快速幂即可。


AC代码:

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <ctime>
#include <vector>
#include <queue>
#include <stack>
#include <deque>
#include <string>
#include <map>
#include <set>
#include <list>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define LL long long
#define fi first
#define se second
#define mem(a,b) memset((a),(b),sizeof(a))

const int MOD=7777777;
const int MAXN=10;

struct Matrix
{
    int a[200][200];
    int n;
    Matrix()
    {
        memset(a,0,sizeof(a));
    }
    void init()
    {
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                a[i][j]=(i==j);
    }
    Matrix operator * (const Matrix &B)const
    {
        Matrix C;
        C.n=n;
        for(int i=0;i<n;i++)
            for(int k=0;k<n;k++)
                for(int j=0;j<n;j++)
                    C.a[i][j]=(C.a[i][j]+1LL*a[i][k]*B.a[k][j]+MOD)%MOD;
        return C;
    }
    Matrix operator ^ (const int &t)const
    {
        Matrix A=(*this),res;
        res.n=n;
        res.init();
        int p=t;
        while(p)
        {
            if(p&1)res=res*A;
            A=A*A;
            p>>=1;
        }
        return res;
    }
};

struct HashMap
{
    const static int mod=419;
    const static int maxn=1010;
    int head[mod];//链表头指针
    int next[maxn];//指向链表下一个节点
    int size;//当前节点数
    int key[maxn];//键
    void clear()
    {
        size=0;
        memset(head,-1,sizeof head);
    }
    inline int insert(int _key)
    {
        int p=_key%mod;//取模后对应的链
        for(int i=head[p];~i;i=next[i])
            if(key[i]==_key)
                return i;
        key[size]=_key;
        next[size]=head[p];
        head[p]=size++;
        return size-1;
    }
}hm;

int N, M;
int tmp_state[MAXN];//保存当前轮廓线状态
int ch[MAXN];//最小表示法使用
int save[8][200][200], num[8];

void decode(int *tmp_state, int n, int key)
{
    for(int i=n-1;i>=0;--i)
    {
        tmp_state[i]=key&3;
        key>>=2;
    }
}

int encode(int *tmp_state, int n)
{
    int cnt=1, key=0;
    mem(ch, -1);
    ch[0]=0;
    for(int i=0;i<n;++i)
    {
        if(ch[tmp_state[i]]==-1)
            ch[tmp_state[i]]=cnt++;
        tmp_state[i]=ch[tmp_state[i]];
        key<<=2;
        key|=tmp_state[i];
    }
    return key;
}

bool check(int now_key, int next_key)//判断两个状态能不能转移
{
    decode(tmp_state, N, now_key);
    int flag=0;//标记格子上边是否有插头
    int cnt=0;
    int k=-1;
    for(int i=0;i<N;++i)
    {
        if(!flag)//格子上边没有插头
        {
            if(!tmp_state[i] && !(next_key&(1<<i)))//左边和右边都没有插头
                return false;
            if(tmp_state[i] && (next_key&(1<<i)))
                continue;
            if(tmp_state[i])
                flag=tmp_state[i];//插头从左边进来,从下边出去
            else flag=-1;//插头从下边进来,从右边出去
            k=i;
        }
        else//格子上边有插头
        {
            if(tmp_state[i] && next_key&(1<<i))//上左右都有插头
                return false;
            if(!tmp_state[i] && !(next_key&(1<<i)))//插头从上边进来,下边出去
                continue;
            if(tmp_state[i])//左边有插头
            {
                if(tmp_state[i]==flag && (next_key || i!=N-1))//只有最后一个格子才能合起来
                    return false;
                if(flag>0)//合并两个插头
                {
                    for(int j=0;j<N;++j)
                        if(tmp_state[j]==tmp_state[i] && j!=i)
                            tmp_state[j]=tmp_state[k];
                    tmp_state[i]=tmp_state[k]=0;
                }
                else//向上延伸
                {
                    tmp_state[k]=tmp_state[i];
                    tmp_state[i]=0;
                }
            }
            else//左边没有插头
            {
                if(flag>0)//向右延伸
                {
                    tmp_state[i]=tmp_state[k];
                    tmp_state[k]=0;
                }
                else//新建一个连通块
                {
                    tmp_state[i]=tmp_state[k]=N+(cnt++);
                }
            }
            flag=0;
        }
    }
    return !flag;
}

void init()
{
    for(N=2;N<=7;++N)//预处理出转移矩阵
    {
        hm.clear();
        mem(tmp_state, 0);
        tmp_state[0]=tmp_state[N-1]=1;
        hm.insert(0);
        hm.insert(encode(tmp_state, N));
        for(int i=1;i<hm.size;++i)
        {
            int key=hm.key[i];
            decode(tmp_state, N, key);
            for(int next_key=0;next_key<(1<<N);++next_key)
                if(check(key, next_key))
                {
                    int j=hm.insert(encode(tmp_state, N));
                    save[N][i][j]=1;
                }
        }
        num[N]=hm.size;
    }
}

int main()
{
    init();
    while(~scanf("%d%d", &N, &M))
    {
        Matrix mat;
        mat.n=num[N];
        memcpy(mat.a, save[N], sizeof save[N]);
        mat=mat^M;
        if(!mat.a[1][0])
            puts("Impossible");
        else printf("%d\n", mat.a[1][0]%MOD);
    }
    
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值