好题,由m的范围知道这肯定是矩阵乘法加速插头dp,关键是怎么写

以往插头dp常用逐格递推,而这道题要求整行逐列递推

这样我们才能构造转移矩阵。

我们可以通过假象一个第0列来将路径转化为回路问题

逐列递推依然使用最小表示法,维护这一列每个格子向右的插头的连通性(最小表示法)

我们可以通过已知状态不断扩展出新的状态(初始显然只有无右插头和顶部底部有右插头两种情况)

对于一个已知列插头状态,我们穷举下一列每一个格子是否有插头,就知道了下一列每个格子是否有左插头和右插头

由于一个格子有且仅有两个插头,因此我们就确定了这一列的情况从而可以不断的扩展出新的合法状态

而最终合法状态不会超过150,接下来矩阵快速幂即可

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm> using namespace std;
typedef long long ll;
const int mo=;
const int has=;
int b[],v[],n,m,t;
struct node
{
int len,st[],p[has],nex[];
void clr()
{
len=;
memset(p,,sizeof(p));
}
int push(int nw)
{
int x=nw%has;
for (int i=p[x]; i>-; i=nex[i])
if (st[i]==nw) return i;
st[++len]=nw;
nex[len]=p[x]; p[x]=len;
return len;
}
} f; struct mat{
int a[][];
friend mat operator *(mat a,mat b)
{
mat c;
for (int i=; i<=t; i++)
for (int j=; j<=t; j++)
{
ll s=;
for (int k=; k<=t; k++) s+=(ll)a.a[i][k]*b.a[k][j];
c.a[i][j]=s%mo;
}
return c;
}
} ans,c; void get(int st)
{
for (int i=n-; i>=; i--)
{
b[i]=st&;
st>>=;
}
} int put()
{
memset(v,,sizeof(v)); v[]=;
int t=,st=;
for (int i=; i<n; i++)
{
if (v[b[i]]==-) v[b[i]]=++t;
st<<=;
st|=v[b[i]];
}
return st;
} bool check(int cur,int nw)
{
get(cur);
int pre=,k=,t=n;
for (int i=; i<n; i++)
{
int x=(nw>>i)&;
if (pre==)
{
if (!b[i]&&!x) return ;
if (b[i]&&x) continue;
if (b[i]) {pre=b[i];b[i]=;}
else pre=-;
k=i;
}
else {
if (b[i]&&x) return ;
if (!b[i]&&!x) continue;
if (b[i])
{
if (b[i]==pre&&(nw!=||i!=n-)) return ;
if (pre>)
{
for (int r=; r<n; r++)
if (i!=r&&b[r]==b[i]) b[r]=pre;
b[i]=;
}
else {b[k]=b[i],b[i]=;}
}
else {
if (pre>) b[i]=pre;
else b[i]=b[k]=++t;
}
pre=;
}
}
return pre==;
} void quick(int n)
{
memset(ans.a,,sizeof(ans.a));
for (int i=; i<=t; i++) ans.a[i][i]=;
while (n)
{
if (n&) ans=ans*c;
c=c*c;
n>>=;
}
} int main()
{
while (scanf("%d%d",&n,&m)!=EOF)
{
memset(b,,sizeof(b));
memset(c.a,,sizeof(c.a));
f.clr();
f.push();
b[]=b[n-]=; f.push(put());
for (int i=; i<=f.len; i++)
for (int j=; j<(<<n); j++)
if (check(f.st[i],j))
{
int k=f.push(put());
c.a[i][k]=;
}
t=f.len;
quick(m);
if (ans.a[][]==) puts("Impossible");
else printf("%d\n",ans.a[][]);
}
}

zoj3256的更多相关文章

  1. [ZOJ3256] Tour in the Castle

    插头DP+矩阵乘法 m喜闻乐见地达到了10^9级别..而n<=7,并且没有障碍..所以列与列之间的转移时一样的..就可以上矩乘了. 感觉自己快没救了..看半天题解还是不懂.. http://ww ...

随机推荐

  1. Hiberante可配置参数

    ###################### ### Query Language ### ###################### ## define query language consta ...

  2. 不使用库函数、自己编写的(strlen、strcpy、strcmp、strcat、memcmp、memcpy、memmove)

    不使用库函数.自己编写的(strlen.strcpy.strcmp.strcat.memcmp.memcpy.memmove) //求字符串长度的函数 int my_strlen(const char ...

  3. 2017 Multi-University Training Contest - Team 2 Puzzle

    题目大意: 给定n, m, p.然后按照一个规则往n*m的方格里填数,最后一个方格是空格,然后玩拼图游戏,问能否复原 规则是:把1~n*m-1的排列中的第1,p+1,2*p+1.....个数依次取出来 ...

  4. [CF912A]Tricky Alchemy

    题意:你有a个黄水晶和b个蓝水晶,要求要x个黄水晶球(2黄),y个绿水晶球(1黄1蓝),z个蓝水晶球(3蓝),问还要多少水晶题解:模拟 C++ Code: #include<cstdio> ...

  5. [洛谷P3937]Changing

    题目大意:有 $n$ 盏灯环形排列,顺时针依次标号为 $1\cdots n$.初始时刻为 $0$ ,初始时刻第 $i$ 盏灯的亮灭 $a_i$, $0$ 表示灭, $1$ 表示亮.下一时刻每盏灯的亮灭 ...

  6. poj 1201 TYVJ 1415 Intervals

    Description: 给定n个闭区间[ai,bi] 和n个整数ci,你需要构造一个集合Z,使得对于任何的i∈[1,n],Z中满足x∈[ai,bi]的x不少于ci个 求这样的整数集合Z至少包含多少个 ...

  7. [poj 1743]差分+后缀数组

    题目链接:http://poj.org/problem?id=1743 首先,musical theme只与前后位置的增减关系有关,而与绝对的数值无关,因此想到做一次差分. 然后对于差分后的数组,找到 ...

  8. AWS nat monitor and route switch script

    This script will monitor another NAT instance and take over its routes if communication with the oth ...

  9. 移动端去掉a标签点击时出现的背景

    之前做移动端的Portal时,手机上测试,点击a标签总是出现一个背景框 在CSS中添加 -webkit-tap-highlight-color: rgba(0, 0, 0, 0);就可以了 a:act ...

  10. 固定width但是有间隔

    <!DOCTYPE > <html> <head> <title></title> <meta name="name&quo ...