[CQOI2007]矩形
题目
点这里看题目。
分析
插头 DP ,考虑枚举一下两块之间的分割线,本质上就是两个端点都在边界上的路径。
DP 过程中,我们将没有端点在边界上面的路径称为 1 路径,反之叫 2 路径。
对于 1 路径,我们不能中途把它连成环,因此 1 路径的插头需要用括号序表示(最小整数也可以,只要能判掉连通性就可以了)。
接着对插头分一下类:
1.一条 1 路径的左端点,编号为 1 。
2.一条 1 路径的右端点,编号为 2 。
3.一条 2 路径的一个端点,编号为 3 。(如果有 3 插头,那么这个 2 路径的另一个端点就在边界上)
由于插头只有 4 种情况,因此我们使用 4 进制来压缩状态。
另外,我们只能在边界上放\(2\)个端点,因此需要再开一维来记录一下。
DP 状态如下:
\(f(i,j,S,c)\):格子\((i,j)\)的轮廓线状态为\(S\),并且在边界上放了\(c\)个端点的方案数。
转移嘛......写起来相当复杂,但是分类讨论本身不难想,所以想看就去代码里找吧qwq逃了。
代码
#include <cstdio>
#include <cstring>
#define l j - 1
#define u j
#define d j - 1
#define r j
#define mov( x ) ( x << 1 )
#define mp( x ) grid[x.first][x.second]
const int MAXN = 20, MAXM = 20, MAXS = ( 1 << 20 ) + 5;
//空间开大,不太清楚为什么qwq
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
struct table
{
int f[MAXS], stk[MAXS], siz;
table() { memset( f, 0, sizeof f ), memset( stk, 0, sizeof stk ), siz = 0; }
int operator [] ( const int indx ) const { return f[stk[indx]]; }
int operator () ( const int indx ) const { return stk[indx]; }
void add( int S, int v ) { if( ! f[S] ) stk[++ siz] = S; f[S] += v; }
void clear() { while( siz ) f[stk[siz --]] = 0; }
};
struct DP_Structure
{
table F[3];
table& operator [] ( const int indx ) { return F[indx]; }
void clear() { for( int i = 0 ; i < 3 ; i ++ ) F[i].clear(); }
}dp[2];
int stk[MAXM] = {}, top;
int state[MAXM], matched[MAXM];
int grid[MAXN][MAXN];
int N, M, pre = 1, nxt = 0;
void prepare() { pre ^= 1, nxt ^= 1, dp[nxt].clear(); }
int get( int x, int y ) { return ( x >> mov( y ) ) & 3; }
int clr( int x, int y ) { return x & ( ~ ( 3 << mov( y ) ) ); }
int reset( int x, int y, int z ) { return clr( x, y ) | ( z << mov( y ) ); }
int dbset( int x, int y, int a, int b = -1 ) { return reset( reset( x, y, a ), y + 1, ~ b ? b : a ); }
void match()
{
for( int i = 0 ; i <= M ; i ++ )
{
if( state[i] == 1 ) stk[++ top] = i;
if( state[i] == 2 ) matched[stk[top]] = i, matched[i] = stk[top --];
}
}
int encode( int *sta )
{
int ret = 0;
for( int i = M ; ~ i ; i -- ) ret = ( ret << 2 ) + sta[i];
return ret;
}
void decode( int *sta, int val )
{
memset( sta, 0, MAXM * 4 );
for( int i = 0 ; i <= M ; i ++ ) sta[i] = val & 3, val >>= 2;
match();
}
int main()
{
int S, v, lef, up, mtL, mtU;
read( N ), read( M );
N ++, M ++;
if( N > M ) N ^= M, M ^= N, N ^= M;
for( int i = 2 ; i < N ; i ++ )
for( int j = 2 ; j < M ; j ++ )
grid[i][j] = 1;
for( int i = 2 ; i < M ; i ++ ) grid[1][i] = grid[N][i] = 2;
for( int i = 2 ; i < N ; i ++ ) grid[i][1] = grid[i][M] = 2;
prepare();
dp[nxt][0].add( 0, 1 );
for( int i = 1 ; i <= N ; i ++ )
{
prepare();
for( int c = 0 ; c <= 2 ; c ++ )
for( int k = 1 ; k <= dp[pre][c].siz ; k ++ )
if( ! get( dp[pre][c]( k ), M ) )
dp[nxt][c].add( dp[pre][c]( k ) << 2, dp[pre][c][k] );
for( int j = 1 ; j <= M ; j ++ )
{
prepare();
for( int c = 0 ; c <= 2 ; c ++ )
for( int k = 1 ; k <= dp[pre][c].siz ; k ++ )
{
S = dp[pre][c]( k ), v = dp[pre][c][k];
lef = get( S, l ), up = get( S, u );
if( grid[i][j] == 0 ) { if( ! lef && ! up ) dp[nxt][c].add( S, v ); continue; }
decode( state, S );
if( grid[i][j] == 2 )
{
dp[nxt][c].add( S, v );
if( c == 2 ) continue;
if( i == 1 ) { if( ! lef && ! up ) dp[nxt][c + 1].add( reset( S, d, 3 ), v ); }
if( j == 1 ) { if( ! lef && ! up ) dp[nxt][c + 1].add( reset( S, r, 3 ), v ); }
//在上边界与左边界放端点,会在轮廓线上新增 3 插头
if( i == N )
{
if( lef || ! up ) continue;
if( up == 3 ) dp[nxt][c + 1].add( dbset( S, d, 0 ), v );
else
{
state[matched[u]] = 3, state[u] = 0;
dp[nxt][c + 1].add( dbset( encode( state ), d, 0 ), v );
}
}
if( j == M )
{
if( ! lef || up ) continue;
if( lef == 3 ) dp[nxt][c + 1].add( dbset( S, d, 0 ), v );
else
{
state[matched[l]] = 3, state[l] = 0;
dp[nxt][c + 1].add( dbset( encode( state ), d, 0 ), v );
}
}
//在下边界与右边界放端点,会与已有插头连接;如果已有插头不是 3 插头,就需要考虑 3 插头移动的情况
}
else
{
if( ! lef && ! up ) dp[nxt][c].add( S, v ), dp[nxt][c].add( dbset( S, d, 1, 2 ), v );
if( lef && ! up ) dp[nxt][c].add( S, v ), dp[nxt][c].add( dbset( S, d, 0, lef ), v );
if( ! lef && up ) dp[nxt][c].add( S, v ), dp[nxt][c].add( dbset( S, d, up, 0 ), v );
if( lef && up )
{
mtL = matched[l], mtU = matched[u];
state[d] = state[r] = 0;
if( lef == 3 && up ^ 3 ) state[mtU] = 3;
if( lef ^ 3 && up == 3 ) state[mtL] = 3;
//考虑 3 插头移动的情况
if( lef == 1 && up == 1 ) state[mtU] = 1;
if( lef == 2 && up == 2 ) state[mtL] = 2;
//正常括号转移
if( lef ^ 1 || up ^ 2 ) dp[nxt][c].add( encode( state ), v );
//需要把会连成环的情况判掉
}
}
}
}
}
write( dp[nxt][2][0] ), putchar( '\n' );
return 0;
}
[CQOI2007]矩形的更多相关文章
- BZOJ1259:[CQOI2007]矩形rect(DFS)
Description 给一个a*b矩形,由a*b个单位正方形组成.你需要沿着网格线把它分成分空的两部分,每部分所有格子连通,且至少有一个格子在原矩形的边界上.“连通”是指任两个格子都可以通过水平或者 ...
- bzoj AC倒序
Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...
- BZOJ 刷题总结(持续更新)
本篇博客按照题号排序(带*为推荐题目) 1008 [HNOI2008]越狱 很经典的题了..龟速乘,龟速幂裸题,, 1010 [HNOI2008]玩具装箱toy* 斜率优化 基本算是裸题. 1012 ...
- [BOT] 一种android中实现“圆角矩形”的方法
内容简介 文章介绍ImageView(方法也可以应用到其它View)圆角矩形(包括圆形)的一种实现方式,四个角可以分别指定为圆角.思路是利用"Xfermode + Path"来进行 ...
- C语言 · 矩形面积交
问题描述 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴.对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积. 输入格式 输入仅包含两行,每行描述一个矩形. 在每行中 ...
- canvas快速绘制圆形、三角形、矩形、多边形
想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...
- [LeetCode] Perfect Rectangle 完美矩形
Given N axis-aligned rectangles where N > 0, determine if they all together form an exact cover o ...
- [LeetCode] Rectangle Area 矩形面积
Find the total area covered by two rectilinear rectangles in a2D plane. Each rectangle is defined by ...
- [LeetCode] Maximal Rectangle 最大矩形
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and ...
随机推荐
- npm run build 时 报 __webpack_public_path__ = window.webpackPublicPath; 中的windows未定义
原本 webpack.js在webpack.config.babel.js同目录下,在app.jsx中引用,用mac打包没问题,但是window就报window未定义,改到src和app.jsx同目录 ...
- 用window.print()打印如何去掉页眉和页脚
用window.print()打印如何去掉页眉和页脚 2007-07-12 11:44:52| 分类: javascript 知识|举报|字号 订阅 <script language= ...
- 【JVM】GC 可达性分析中哪些算是GC ROOT?
至今为止,我基本上发现网上没有几个博客说的很明白的,今天我在这里斗胆总结一下,各位大佬,如有错误,还望指责 ^ _ ^ 首先那肯定还得是看看概念了,在JVM中,什么可以作为GC Root呢? 虚拟机栈 ...
- PHP设计模式(一)
1)工厂模式 工厂模式是用工厂方法生成对象,而不是直接new一个对象.假设我们在Config命名空间下有一个名叫Db的数据库操作类,用普通的方法,如果我们想去创建一个Db的对象,我们会直接new一个出 ...
- 对 getopts 的理解
getopts 格式 1 #!/bin/bash 2 echo "begin index is $OPTIND" 3 echo "begin ARG is $OPTARG ...
- Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.26/images/j..........
如果你在执行 docker images 出现这样的问题 注意:把你的用户切到root下
- [Wireshark]_003_电子邮件抓包分析
电子邮件是我们的生活工作中经常使用的一种服务,用来联系世界各地的朋友,客户.下面我们就用Wireshark对电子邮件进行抓包. 准备工作: 邮件客户端一款(Outlook,Foxmail,KooMai ...
- 用python编写测试脚本
def f(n): """ >>>f(1) 1用例 >>>f(2) 2用例 ...... >>>f(n) n用例 & ...
- harbor越权漏洞(CVE-2019-16097)
漏洞介绍 这个漏洞可以在注册发送post包时,加入has_admin_role:true就可以直接注册成为管理员,下图可以看看user的结构: 有很多属性,此处我们关注的是"HasAdmin ...
- jchdl - RTL实例 - And2And(结构体嵌套的使用)
https://mp.weixin.qq.com/s/PQIPkDymvcGc_re8ux50vA 结构体可以嵌套使用. 参考链接 https://github.com/wjcdx/jchdl ...