好题,由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. esayui combotree 只能选择子节点

    esayui combotree 只能选择子节点用onBeforeSelect:参数是node,节点被选中之前触发,返回false取消选择动作. 网上找了好多都没一个可用的,要想知道他是子节点还是根节 ...

  2. 关于socket的疑问

    一直感觉一端发送数据,另一端接受数据很不可思议的事情,如果不能即时地读走会导致什么后果呢? 其实socket读出来的数据,你自己看着办,里面的数据是什么格式你自己去解析,用户可以基于TCP去实现你自己 ...

  3. Configuring Dojo with dojoConfig - The Dojo Toolkit

    转载自Dojo官网 Configuring Dojo with dojoConfig The dojoConfig object (formerly djConfig) allows you to s ...

  4. 论 Web 前端加密的意义

    论 Web 前端加密的意义 Web前端密码加密是否有意义? https://www.zhihu.com/question/25539382 https://blog.csdn.net/hla19910 ...

  5. BZOJ 2178 圆的面积并 ——Simpson积分

    [题目分析] 史上最良心样例,史上最难调样例. Simpson积分硬上. 听说用long double 精度1e-10才能过. 但是double+1e-6居然过了. [代码] #include < ...

  6. 安卓titlebar的组合控件使用

    http://blog.csdn.net/itachi85/article/details/51435187

  7. Terminals Project

    https://github.com/Terminals-Origin Terminals Project Terminals is a secure, multi tab terminal serv ...

  8. java 构造函数问题

    1.构造函数什么时候被调用,被谁调用? 转摘:http://bbs.csdn.net/topics/350231037 当然,只有在NEW的时候,才会真正的创建这个对象,只有在创建时才会调用该类的构造 ...

  9. linux+GraphicsMagick 安装

    转摘自:http://blog.csdn.net/fhqsse220/article/details/12995763 GraphicsMagick 安装 下载软件:download:ftp://ft ...

  10. JavaBean定义、JSP中使用以及内省操作

        Apache commons 一系列的开源工具室非常值得学习的实现. 一 JavaBean定义     JavaBean是一种可重复使用.且跨平台的软件组件.JavaBean可分为两种:一种是 ...