题解  

  这题是一道非常好的插头题,与一般的按格转移的题目不同,由于m很大,要矩阵乘法,这题需要你做一个按列转移的插头DP。

  按列转移多少与按格转移不同,但大体上还是基于连通性进行转移。每一列只有右插头是对下一列的转移有影响的,那么我们只需要记录每一列的右插头的连通情况,用最小表示法表示为当前列的状态。在转移的时候,我们只知道上一列的右插头,即本列的左插头的情况,而上插头还需要自己记一个标记。

  那么我们具体分析:

  1、不存在上插头

    1、同时存在左插头和右插头,不需要修改当前插头,直接把上一列的右插头当做当前列的右插头

    2、只在左插头,即从上一列的某一个连通块转移过来,记录连通块。(左下插头)

    3、只在右插头,即此为一个新的连通块,打上标记,表明这是一个新的连通块。(右下插头)

  2、存在上插头

    1、同时存在左插头和右插头,一个格子里有三个插头,非法状态

    2、都不存在左插头和右插头,不需要修改当前插头,即从上往下。

    3、存在左插头

      1、上插头和左插头同属一个连通块,但不在最终状态(没有右插头)的右下角的格子里出现,非法状态

      2、上插头是左下插头,合并连通块,并删除这两个插头(这个合并比较特殊,因为两个都是已知的连通块,具体画图比较清晰)

      3、上插头是右下插头,合并连通块,删掉当前插头

    4、不存在左插头

      1、上插头是左下插头,合并连通块,删除左下插头

      2、上插头是右下插头,合并为新的连通块

  具体情况还是自己动手画图比较清晰。

  然后就到了矩乘的部分。首先考虑构造矩阵,g[i][j] = 1表示i状态能推到j状态,因此我们只需要枚举这些状态,一个一个判转移就可以了。

  初始的情况下,只存在全空的状态和0和N-1有插头的情况,因此答案就是矩阵快速幂后的ans[1][0],即从初始状态推向终止状态。

程序

 #include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm> using namespace std; #define REP(i, a, b) for (int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define DWN(i, a, b) for (int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define mset(a, b) memset(a, b, sizeof(a))
typedef long long LL;
const int MAXD = , HASH = , STATE = , MOD = ;
int n, m, code[MAXD], ch[MAXD];
struct HASHMAP
{
int head[HASH], nxt[STATE], state[STATE], siz;
void clear() { siz = , mset(head, -); }
int push(int x)
{
int pos = x%HASH, i = head[pos];
for (; i != -; i = nxt[i])
if (state[i] == x) return i;
state[siz] = x;
nxt[siz] = head[pos], head[pos] = siz++;
return siz-;
}
}hm;
struct Matrix
{
int mat[][], D;
Matrix operator * (const Matrix &AI) const
{
Matrix ret; ret.D = D;
REP(i, , D)
REP(j, , D)
{
LL sum = ;
REP(k, , D) sum += (LL)mat[i][k]*AI.mat[k][j];
ret.mat[i][j] = sum%MOD;
}
return ret;
}
}rc[], A, B; void decode(int x)
{
DWN(i, n, ) code[i] = x&, x >>= ;
} int encode()
{
int cnt = , ret = ;
mset(ch, -), ch[] = ;
REP(i, , n)
{
if (ch[code[i]] == -) ch[code[i]] = ++cnt;
ret <<= , ret |= ch[code[i]];
}
return ret;
} bool check(int st, int nxt)
{
decode(st);
int up = , k, cnt = ;
REP(i, , n)
{
if (up == )
{
if (!code[i] && !(nxt&(<<(i-)))) return false;
if (code[i] && (nxt&(<<(i-)))) continue ;
if (code[i]) up = code[i];
else up = -;
k = i;
}
else
{
if (code[i] && (nxt&(<<(i-)))) return false;
if (!code[i] && !(nxt&(<<(i-)))) continue ;
if (code[i])
{
if (up == code[i] && !(nxt&(<<(i-))) && (nxt || i != n)) return false;
if (up != -)
{
REP(j, , n) if (code[j] == code[i] && j != i) code[j] = code[k];
code[i] = code[k] = ;
}
else code[k] = code[i], code[i] = ;
}
else
{
if (up != -) code[i] = code[k], code[k] = ;
else code[i] = code[k] = n+(++cnt);
}
up = ;
}
}
if (up) return false;
return true;
} void init()
{
if (rc[n].D != )
{ B = rc[n]; return ; }
mset(rc[n].mat, );
hm.clear(), hm.push();
mset(code, ), code[] = code[n] = , hm.push(encode());
decode(hm.state[]);
for (int i = ; i < hm.siz; ++i)
REP(nxt, , ((<<n)-))
if (check(hm.state[i], nxt))
{
int j = hm.push(encode());
rc[n].mat[i][j] ++;
}
rc[n].D = hm.siz-;
B = rc[n];
} void work()
{
mset(A.mat, ); A.D = B.D;
REP(i, , A.D) A.mat[i][i] = ;
while (m > )
{
if (m&) A = A*B;
B = B*B, m >>= ;
}
if (!A.mat[][]) puts("Impossible");
else printf("%d\n", A.mat[][]);
} int main()
{
REP(i, , ) rc[i].D = ;
while (~scanf("%d %d", &n, &m))
init(), work();
return ;
}

ZOJ 3256 Tour in the Castle 插头DP 矩阵乘法的更多相关文章

  1. 【bzoj2004】[Hnoi2010]Bus 公交线路 状压dp+矩阵乘法

    题目描述 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计 ...

  2. 【bzoj3329】Xorequ 数位dp+矩阵乘法

    题目描述 输入 第一行一个正整数,表示数据组数据 ,接下来T行每行一个正整数N 输出 2*T行第2*i-1行表示第i个数据中问题一的解, 第2*i行表示第i个数据中问题二的解, 样例输入 1 1 样例 ...

  3. ZOJ - 3216:Compositions (DP&矩阵乘法&快速幂)

    We consider problems concerning the number of ways in which a number can be written as a sum. If the ...

  4. 【BZOJ-4386】Wycieczki DP + 矩阵乘法

    4386: [POI2015]Wycieczki Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 197  Solved: 49[Submit][Sta ...

  5. Luogu P4643 【模板】动态dp(矩阵乘法,线段树,树链剖分)

    题面 给定一棵 \(n\) 个点的树,点带点权. 有 \(m\) 次操作,每次操作给定 \(x,y\) ,表示修改点 \(x\) 的权值为 \(y\) . 你需要在每次操作之后求出这棵树的最大权独立集 ...

  6. LOJ.6074.[2017山东一轮集训Day6]子序列(DP 矩阵乘法)

    题目链接 参考yww的题解.本来不想写来但是他有一些笔误...而且有些地方不太一样就写篇好了. 不知不觉怎么写了这么多... 另外还是有莫队做法的...(虽然可能卡不过) \(60\)分的\(O(n^ ...

  7. 【BZOJ 3326】[Scoi2013]数数 数位dp+矩阵乘法优化

    挺好的数位dp……先说一下我个人的做法:经过观察,发现这题按照以往的思路从后往前递增,不怎么好推,然后我就大胆猜想,从前往后推,发现很好推啊,维护四个变量,从开始位置到现在有了i个数 f[i]:所有数 ...

  8. bzoj 3329: Xorequ【数位dp+矩阵乘法】

    注意第一问不取模!!! 因为a+b=a|b+a&b,a^b=a|b-a&b,所以a+b=a^b+2(a&b) x^3x==2x可根据异或的性质以转成x^2x==3x,根据上面的 ...

  9. luogu P4719 【模板】动态 DP 矩阵乘法 + LCT

    方法二:LCT+矩阵乘法 上文中,我们用线段树来维护重链上的各种矩阵转移. 第二种方法是将树链剖分替换为动态树. 我们知道,矩阵乘法 $\begin{bmatrix} F_{u,0} & F_ ...

随机推荐

  1. 25、Python垃圾回收机制?

    Python的GC模块主要运用了“引用计数”(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过“标记-清除”(mark and sweep)解决容器对象可能产生的 ...

  2. 生产环境手把手部署ERC20智能合约

    工具 rimex http://remix.ethereum.org/ metamask https://metamask.io/ ERC20 代码 https://github.com/OpenZe ...

  3. 29、最小的K个数

    一.题目 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 二.解法 import java.util.ArrayList; ...

  4. FPGA编码规则检查表

    FPGA编码规则检查表 -----------------------摘自<FPGA软件测试与评价技术> 中国电子信息产业发展研究院 | 编著 1.一个单独的文件应该只包含一个单独的mod ...

  5. CentOS 6.6下目录结构及其主要作用

    今天我们总结一下CentOS 6.6的linux的目录结构,一个系统的目录众多,这里我们主要认识一下,根目录下的主要目录,首先我们可以通过tree命令查看一次根目录下一层目录都有什么目录, 补充:不能 ...

  6. makefile初步制作,arm-linux- (gcc/ld/objcopy/objdump)详解【转】

    转自:http://www.cnblogs.com/lifexy/p/7065175.html 在linux中输入vi Makefile 来实现创建Makefile文件 注意:命令行前必须加TAB键 ...

  7. 用C#实现对MSSqlServer数据库的增删改查---Server层(WaterLevelRecordServer.cs、DeviceRecordServer.cs)

    抛开现实的残酷与无奈,对技术孜孜不倦的追求,从专注到执着,从疯狂到忘我,始终坚信代码可以改变世界,更能改变自己的人生. WaterLevelRecordServer.cs using System; ...

  8. 健身VS不健身,完全是两种不同的人生!

    这两天一组同龄人合照 刷爆了国内健身圈, 图左是一位67岁的老人, 图右是67岁的健美运动员杨新民老师 相同年龄, 但从外观上有着强烈的距离感! 让多人不禁感叹,健身和不健身, 简直就是两种状态,两种 ...

  9. Xcode及模拟器SDK下载

    http://blog.csdn.net/zhangao0086/article/details/38491271 吐槽下,百度打着无限分享的旗号,却又让分享资源过期,让分享者持续维护 如果你嫌在Ap ...

  10. PHP发送邮件:如何自定义reply-to头部以及附件

    虽然有现成的类库(如PEAR)可以很方便地实现附件添加和发送,但是对于一些小站点(服务器硬件.网站规模都不理想),安装PEAR可能会带来不必要的负担,降低WEB程序运行效率. 通过对邮件格式的认识,我 ...