ZOJ 3256 Tour in the Castle 解题报告(插头DP)

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


    解题报告:那个M的取值很夸张。不过第一意识还是想到了二分快速幂。至于怎么建立模型,构造矩阵,倒是想了挺久。

    我们可以先枚举所有可能的状态,以此状态为一行的初状态,用插头DP的方式计算所有可能的一行后的状态及其数量,以此构造矩阵。

    最后一行单独处理。代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=1117;
const int MOD=7777777;
const int L=2;//	4进制
int now,pre;
int n,m;
int size;
struct Node
{
	int H[maxn];
	int S[maxn];
	int N[maxn];
	int size;
	void init()
	{
		size=0;
		memset(H,-1,sizeof(H));
	}
	void push(int SS,int num)
	{
		int s=SS%maxn;
		while( ~H[s] && S[H[s]]!=SS )
			s=(s+1)%maxn;
		if( ~H[s] )
		{
			N[H[s]]=(N[H[s]]+num)%MOD;
			return;
		}

		S[size]=SS;
		N[size]=num%MOD;
		H[s]=size++;
	}
	int get(int SS)
	{
		int s=SS%maxn;
		while( ~H[s] && S[H[s]]!=SS )
			s=(s+1)%maxn;
		if( ~H[s] )
			return N[H[s]];
		return 0;
	}
	int getIndex(int SS)
	{
		int s=SS%maxn;
		while( ~H[s] && S[H[s]]!=SS )
			s=(s+1)%maxn;
		if( ~H[s] )
			return H[s];
		return -1;
	}
} dp[3];

int get(int S,int p)
{
	return (S>>(p*L))&((1<<L)-1);
}

void set(int &S,int p,int v)
{
	S^=get(S,p)<<(p*L);
	S^=v<<(p*L);
}

struct Martrix
{
	int a[128][128];
	Martrix()
	{
		memset(a,0,sizeof(a));
	}
	Martrix operator*(const Martrix& cmp) const
	{
		Martrix c;
		for(int i=0;i<size;i++)
			for(int j=0;j<size;j++)
				for(int k=0;k<size;k++)	// 矩阵内部乘法越界问题
					c.a[i][j]=(c.a[i][j]+(int)(((LL)a[i][k]*cmp.a[k][j])%MOD))%MOD;
		return c;
	}
};

void init()
{
	dp[2].init();
	int total=(1<<(n*2));
	for(int S=0;S<total;S++)
	{
		int v;
		int one=0,two=0;
		for(int j=0;j<n;j++)
		{
			v=get(S,j);
			if(v==3) break;
			if(v==2) two++;
			if(v==1) one++;
			if(two>one) break;
		}
		if(v==3) continue;
		if(one!=two) continue;
		dp[2].push(S<<L,1);
	}
	size=dp[2].size;
}

int line(int SS,int nn)
{
	now=1;
	pre=0;
	int ans=0;
	dp[now].init();
	dp[now].push(SS,nn);

	for(int j=0;j<n;j++)
	{
		swap(now,pre);
		dp[now].init();
		for(int s=0;s<dp[pre].size;s++)
		{
			int S=dp[pre].S[s];
			int num=dp[pre].N[s];
			int p=get(S,j);
			int q=get(S,j+1);

			if(p==0 && q==0)
			{
				if(j<n-1)
				{
					set(S,j,1);
					set(S,j+1,2);
					dp[now].push(S,num);
				}
			}
			else if((p>0)^(q>0))
			{
				if(j+(q>0)<n)
					dp[now].push(S,num);
				set(S,j,q);
				set(S,j+1,p);
				if(j+(p>0)<n)
					dp[now].push(S,num);
			}
			else if(p==1 && q==2)
			{
				set(S,j,0);
				set(S,j+1,0);
				if(j==n-1 && S==0)
					ans=(ans+num)%MOD;
			}
			else if(p==2 && q==1)
			{
				set(S,j,0);
				set(S,j+1,0);
				dp[now].push(S,num);
			}
			else if(p==1 && q==1)
			{
				int find=1;
				for(int k=j+2;k<=n;k++)
				{
					int v=get(S,k);
					if(v==1) find++;
					else if(v==2) find--;
					if(find==0)
					{
						set(S,j,0);
						set(S,j+1,0);
						set(S,k,1);
						dp[now].push(S,num);
						break;
					}
				}
			}
			else if(p==2 && q==2)
			{
				int find=1;
				for(int k=j-1;k>=0;k--)
				{
					int v=get(S,k);
					if(v==2) find++;
					else if(v==1) find--;
					if(find==0)
					{
						set(S,j,0);
						set(S,j+1,0);
						set(S,k,2);
						dp[now].push(S,num);
						break;
					}
				}
			}
		}
	}
	for(int s=0;s<dp[now].size;s++)
		dp[now].S[s]<<=L;
	return ans;
}

void quick(Martrix& a,int num)
{
	Martrix res=a;
	num--;
	while(num)
	{
		if(num&1)
			res=a*res;
		a=a*a;
		num>>=1;
	}
	a=res;
}

int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		if(m==1) {puts("1");continue;}
		if(n%2 && m%2==0) {puts("Impossible");continue;}

		init();
		Martrix a;

		for(int i=0;i<size;i++)
		{
			line(dp[2].S[i],1);
			for(int s=0;s<dp[now].size;s++)
				a.a[i][dp[2].getIndex(dp[now].S[s])]=dp[now].N[s];
		}

		quick(a,m-1);
		int STA=0;
		set(STA,1,1);
		set(STA,n,2);
		int no=dp[2].getIndex(STA);
		int ans=0;
		bool success=false;
		for(int i=0;i<size;i++)
		{
			ans+=line(dp[2].S[i],a.a[no][i]);
			if(ans) success=true;
			ans%=MOD;
		}
		if(success)
			printf("%d\n",ans);
		else
			puts("Impossible");
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值