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
动态规划 插头DP 矩阵乘法 脑洞题
辣鸡破题蒸鹅心
问从起点到终点的哈密顿路径数(如图所示)
先把图顺时针旋转90°,再在最上面虚拟一行(这行只有最左边和最右边有向下插头), 显然这可以一行一行做插头DP。
但是由于行数太多了,会T。
普通DP会T的时候的优化方法之一是矩阵乘法,好巧这里就是用矩阵乘法。
将(旋转后的)一行设为一个状态,标记哪些位置有从上面来的插头
先暴力枚举判断出每个状态可以转移到哪些状态(合法状态最多130+种),然后建立转移矩阵,大力自乘到m次方,出解……
第一遍写了全程状态压位的普通写法,然后蜜汁爆炸调不对。挣扎好久之后放弃,从网上找到了神奇的数组记录插头的方法,看着直观又好写。
然后继续调错调错……
全程挣扎三个多小时
1 /*by SilverN*/ 2 #include<algorithm> 3 #include<iostream> 4 #include<cstring> 5 #include<cstdio> 6 #include<cmath> 7 #include<vector> 8 #include<map> 9 #define LL long long 10 using namespace std; 11 const int mod=7777777; 12 const int mxn=100010; 13 int read(){ 14 int x=0,f=1;char ch=getchar(); 15 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 16 while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();} 17 return x*f; 18 } 19 int SZ,ed; 20 int n,m; 21 struct Mat{ 22 LL x[135][135]; 23 void init(){ 24 memset(x,0,sizeof x); 25 for(int i=0;i<=SZ;i++)x[i][i]=1; 26 return; 27 } 28 friend Mat operator * (Mat a,Mat b){ 29 Mat res; 30 for(int i=0;i<=SZ;i++) 31 for(int j=0;j<=SZ;j++){ 32 res.x[i][j]=0; 33 for(int k=1;k<=SZ;k++) 34 res.x[i][j]+=a.x[i][k]*b.x[k][j]; 35 res.x[i][j]%=mod; 36 } 37 return res; 38 } 39 }a,bas; 40 map<int,int>mp; 41 int cnt=0; 42 int code[20]; 43 void decode(int x){ 44 for(int i=n;i;i--)code[i]=x&3,x>>=2; 45 } 46 int encode(){ 47 int w=0,ch[22]; 48 memset(ch,-1,sizeof ch); 49 ch[0]=w++; 50 int res=0; 51 for(int i=1;i<=n;i++){ 52 if(ch[code[i]]==-1)ch[code[i]]=w++; 53 code[i]=ch[code[i]]; 54 res=(res<<2)|code[i]; 55 } 56 return res; 57 } 58 bool DP(int s,int T){ 59 decode(s); 60 int left=0,cnt=0,k; 61 for(int i=1;i<=n;i++){ 62 int v=T&(1<<(n-i));//当前位置有无上插头 63 if(!left){ 64 if(!code[i] && !v)return 0; 65 if(!code[i] && v)left=-1; 66 if(code[i] && !v)left=code[i]; 67 if(code[i] && v)continue;//竖线 68 k=i; 69 } 70 else{ 71 if(!code[i] && !v)continue;//平 72 else if(!code[i] && v){ 73 if(left>0)code[k]=0,code[i]=left;//右+上 左+下 74 else code[k]=code[i]=4+(cnt++);//右+下 75 left=0; 76 } 77 else if(code[i] && !v){ 78 if(code[i]==left && (T || i!=n))return 0; 79 if(left>0){//k 右+上 i 左+上 80 for(int j=1;j<=n;j++){ 81 if(code[j]==left)code[j]=code[i]; 82 } 83 code[k]=code[i]=0;left=0; 84 } 85 else{ 86 code[k]=code[i]; 87 code[i]=0; 88 left=0; 89 } 90 } 91 else if(code[i] && v)return 0; 92 } 93 } 94 return left==0; 95 } 96 void Calc(int k){ 97 bas.init(); 98 while(k){ 99 if(k&1)bas=bas*a; 100 a=a*a; 101 k>>=1; 102 } 103 return; 104 } 105 //map<int,int>::iterator it; 106 void init(){ 107 memset(a.x,0,sizeof a.x); 108 memset(code,0,sizeof code); 109 mp.clear();// 110 code[1]=code[n]=1; 111 cnt=0; 112 ed=1<<n; 113 vector<int>v; 114 mp[0]=0;//目标状态 115 mp[encode()]=++cnt; 116 v.push_back(0); 117 v.push_back(encode()); 118 for(int i=1;i<v.size();i++){ 119 int st=v[i]; 120 for(int j=0;j<ed;j++){ 121 if(DP(st,j)){ 122 int st2=encode(); 123 if(mp.find(st2)==mp.end()){ 124 mp[st2]=++cnt; 125 v.push_back(st2); 126 } 127 // printf("st2:%d\n",st2); 128 // printf("st:%d j:%d u:%d v:%d\n",st,j,i,mp[st2]); 129 a.x[i][mp[st2]]=1; 130 } 131 } 132 } 133 SZ=cnt; 134 135 for(int i=0;i<=SZ;i++){ 136 for(int j=0;j<=SZ;j++){ 137 printf("%d ",a.x[i][j]); 138 } 139 printf("\n"); 140 } 141 printf("fin\n"); 142 143 return; 144 } 145 void solve(){ 146 Calc(m); 147 /* for(int i=0;i<=SZ;i++){ 148 for(int j=0;j<=SZ;j++){ 149 printf("%d ",bas.x[i][j]); 150 } 151 printf("\n"); 152 }*/ 153 if(!bas.x[1][0])printf("Impossible\n"); 154 else printf("%d\n",bas.x[1][0]); 155 return; 156 } 157 int main(){ 158 int i,j; 159 while(scanf("%d%d",&n,&m)!=EOF){ 160 if((m&1)==0 && (n&1)){ 161 printf("Impossible\n"); 162 continue; 163 } 164 init(); 165 solve(); 166 } 167 return 0; 168 }