ZOJ 3256 Tour in the Castle 插头DP 矩阵乘法
题解
这题是一道非常好的插头题,与一般的按格转移的题目不同,由于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 矩阵乘法的更多相关文章
- 【bzoj2004】[Hnoi2010]Bus 公交线路 状压dp+矩阵乘法
题目描述 小Z所在的城市有N个公交车站,排列在一条长(N-1)km的直线上,从左到右依次编号为1到N,相邻公交车站间的距离均为1km. 作为公交车线路的规划者,小Z调查了市民的需求,决定按下述规则设计 ...
- 【bzoj3329】Xorequ 数位dp+矩阵乘法
题目描述 输入 第一行一个正整数,表示数据组数据 ,接下来T行每行一个正整数N 输出 2*T行第2*i-1行表示第i个数据中问题一的解, 第2*i行表示第i个数据中问题二的解, 样例输入 1 1 样例 ...
- ZOJ - 3216:Compositions (DP&矩阵乘法&快速幂)
We consider problems concerning the number of ways in which a number can be written as a sum. If the ...
- 【BZOJ-4386】Wycieczki DP + 矩阵乘法
4386: [POI2015]Wycieczki Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 197 Solved: 49[Submit][Sta ...
- Luogu P4643 【模板】动态dp(矩阵乘法,线段树,树链剖分)
题面 给定一棵 \(n\) 个点的树,点带点权. 有 \(m\) 次操作,每次操作给定 \(x,y\) ,表示修改点 \(x\) 的权值为 \(y\) . 你需要在每次操作之后求出这棵树的最大权独立集 ...
- LOJ.6074.[2017山东一轮集训Day6]子序列(DP 矩阵乘法)
题目链接 参考yww的题解.本来不想写来但是他有一些笔误...而且有些地方不太一样就写篇好了. 不知不觉怎么写了这么多... 另外还是有莫队做法的...(虽然可能卡不过) \(60\)分的\(O(n^ ...
- 【BZOJ 3326】[Scoi2013]数数 数位dp+矩阵乘法优化
挺好的数位dp……先说一下我个人的做法:经过观察,发现这题按照以往的思路从后往前递增,不怎么好推,然后我就大胆猜想,从前往后推,发现很好推啊,维护四个变量,从开始位置到现在有了i个数 f[i]:所有数 ...
- 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,根据上面的 ...
- luogu P4719 【模板】动态 DP 矩阵乘法 + LCT
方法二:LCT+矩阵乘法 上文中,我们用线段树来维护重链上的各种矩阵转移. 第二种方法是将树链剖分替换为动态树. 我们知道,矩阵乘法 $\begin{bmatrix} F_{u,0} & F_ ...
随机推荐
- javascript中的数组去重
1.方法一:双层循环,外层循环元素,内层循环做比较,若相同则跳过,不同则加入结果集中,获取没重复的最右侧的值放入数组中 Array.prototype.distinct = function(){ v ...
- perl6 登录phpmyadmin
use HTTP::UserAgent; my $ua = HTTP::UserAgent.new; my $url = 'http://localhost/phpMyAdmin/index.php' ...
- 我的Apache又挂了之apache错误:server's fully qualified domain name, using 127.0.0.1. Set the 'ServerName'
表示物理机装Apache然后有时候关机会忘了关闭Apache然后长此以往会导致各种Apache起不来的缘故,上一次已经出现过一次.今天又出现了 再次记录一下解决的方法. 1.查看错误日志 /var/l ...
- python基础===用9种方式生成新的对象
class Point: def __init__(self, x, y): self.x = x self.y = y point1 = Point(1, 2) point2 = eval(&quo ...
- 64_n2
nodejs-from-0.1.3-4.fc26.noarch.rpm 11-Feb-2017 15:01 9982 nodejs-from2-2.1.0-6.fc26.noarch.rpm 11-F ...
- python操作adb代码
adb命令的练习 #!/usr/bin/env python # encoding: utf-8 import os import re nameplt=re.compile("packag ...
- 147.Insertion Sort List---链表排序(直接插入)
题目链接 题目大意:对链表进行插入排序. 解法:直接插入排序.代码如下(耗时40ms): public ListNode insertionSortList(ListNode head) { List ...
- Python——文件打开模式辨析
版权声明:本文系原创,转载请注明出处及链接. Python中,open()函数打开文件时打开模式如r.r+ .w+.w.a.a+有何不同 r 只能读 r+ 可读可写,不会创建不存在的文件.如果直接写文 ...
- 关于HTML5服务器发送事件(SSE)
最近在看 W3School 上关于 HTML 5 的教程.在看到 HTML 5 服务器发送事件 ( SSE, server-sent event ) 时,没怎么弄明白示例代码是怎么回事,寻找其他教程, ...
- linux的fwrite()使用方法,当前时间写入文本的程序
fwrite函数 1.函数功能 用来读写一个数据块. 2.一般调用形式 fwrite(buffer,size,count,fp); 3.说明 (1)buffer:是一个指针,对fread来说,它是读入 ...