好题,由m的范围知道这肯定是矩阵乘法加速插头dp,关键是怎么写
以往插头dp常用逐格递推,而这道题要求整行逐列递推
这样我们才能构造转移矩阵。
我们可以通过假象一个第0列来将路径转化为回路问题
逐列递推依然使用最小表示法,维护这一列每个格子向右的插头的连通性(最小表示法)
我们可以通过已知状态不断扩展出新的状态(初始显然只有无右插头和顶部底部有右插头两种情况)
对于一个已知列插头状态,我们穷举下一列每一个格子是否有插头,就知道了下一列每个格子是否有左插头和右插头
由于一个格子有且仅有两个插头,因此我们就确定了这一列的情况从而可以不断的扩展出新的合法状态
而最终合法状态不会超过150,接下来矩阵快速幂即可
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<algorithm> 5 6 using namespace std; 7 typedef long long ll; 8 const int mo=7777777; 9 const int has=419; 10 int b[15],v[15],n,m,t; 11 struct node 12 { 13 int len,st[1010],p[has],nex[1010]; 14 void clr() 15 { 16 len=0; 17 memset(p,255,sizeof(p)); 18 } 19 int push(int nw) 20 { 21 int x=nw%has; 22 for (int i=p[x]; i>-1; i=nex[i]) 23 if (st[i]==nw) return i; 24 st[++len]=nw; 25 nex[len]=p[x]; p[x]=len; 26 return len; 27 } 28 } f; 29 30 struct mat{ 31 int a[150][150]; 32 friend mat operator *(mat a,mat b) 33 { 34 mat c; 35 for (int i=1; i<=t; i++) 36 for (int j=1; j<=t; j++) 37 { 38 ll s=0; 39 for (int k=1; k<=t; k++) s+=(ll)a.a[i][k]*b.a[k][j]; 40 c.a[i][j]=s%mo; 41 } 42 return c; 43 } 44 } ans,c; 45 46 void get(int st) 47 { 48 for (int i=n-1; i>=0; i--) 49 { 50 b[i]=st&3; 51 st>>=2; 52 } 53 } 54 55 int put() 56 { 57 memset(v,255,sizeof(v)); v[0]=0; 58 int t=0,st=0; 59 for (int i=0; i<n; i++) 60 { 61 if (v[b[i]]==-1) v[b[i]]=++t; 62 st<<=2; 63 st|=v[b[i]]; 64 } 65 return st; 66 } 67 68 bool check(int cur,int nw) 69 { 70 get(cur); 71 int pre=0,k=0,t=n; 72 for (int i=0; i<n; i++) 73 { 74 int x=(nw>>i)&1; 75 if (pre==0) 76 { 77 if (!b[i]&&!x) return 0; 78 if (b[i]&&x) continue; 79 if (b[i]) {pre=b[i];b[i]=0;} 80 else pre=-1; 81 k=i; 82 } 83 else { 84 if (b[i]&&x) return 0; 85 if (!b[i]&&!x) continue; 86 if (b[i]) 87 { 88 if (b[i]==pre&&(nw!=0||i!=n-1)) return 0; 89 if (pre>0) 90 { 91 for (int r=0; r<n; r++) 92 if (i!=r&&b[r]==b[i]) b[r]=pre; 93 b[i]=0; 94 } 95 else {b[k]=b[i],b[i]=0;} 96 } 97 else { 98 if (pre>0) b[i]=pre; 99 else b[i]=b[k]=++t; 100 } 101 pre=0; 102 } 103 } 104 return pre==0; 105 } 106 107 void quick(int n) 108 { 109 memset(ans.a,0,sizeof(ans.a)); 110 for (int i=1; i<=t; i++) ans.a[i][i]=1; 111 while (n) 112 { 113 if (n&1) ans=ans*c; 114 c=c*c; 115 n>>=1; 116 } 117 } 118 119 int main() 120 { 121 while (scanf("%d%d",&n,&m)!=EOF) 122 { 123 memset(b,0,sizeof(b)); 124 memset(c.a,0,sizeof(c.a)); 125 f.clr(); 126 f.push(0); 127 b[0]=b[n-1]=1; f.push(put()); 128 for (int i=2; i<=f.len; i++) 129 for (int j=0; j<(1<<n); j++) 130 if (check(f.st[i],j)) 131 { 132 int k=f.push(put()); 133 c.a[i][k]=1; 134 } 135 t=f.len; 136 quick(m); 137 if (ans.a[2][1]==0) puts("Impossible"); 138 else printf("%d\n",ans.a[2][1]); 139 } 140 }
原文:http://www.cnblogs.com/phile/p/6354580.html