题目

  点这里看题目。

分析

  插头 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]矩形的更多相关文章

  1. BZOJ1259:[CQOI2007]矩形rect(DFS)

    Description 给一个a*b矩形,由a*b个单位正方形组成.你需要沿着网格线把它分成分空的两部分,每部分所有格子连通,且至少有一个格子在原矩形的边界上.“连通”是指任两个格子都可以通过水平或者 ...

  2. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  3. BZOJ 刷题总结(持续更新)

    本篇博客按照题号排序(带*为推荐题目) 1008 [HNOI2008]越狱 很经典的题了..龟速乘,龟速幂裸题,, 1010 [HNOI2008]玩具装箱toy* 斜率优化 基本算是裸题. 1012 ...

  4. [BOT] 一种android中实现“圆角矩形”的方法

    内容简介 文章介绍ImageView(方法也可以应用到其它View)圆角矩形(包括圆形)的一种实现方式,四个角可以分别指定为圆角.思路是利用"Xfermode + Path"来进行 ...

  5. C语言 · 矩形面积交

    问题描述 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴.对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积. 输入格式 输入仅包含两行,每行描述一个矩形. 在每行中 ...

  6. canvas快速绘制圆形、三角形、矩形、多边形

    想看前面整理的canvas常用API的同学可以点下面: canvas学习之API整理笔记(一) canvas学习之API整理笔记(二) 本系列文章涉及的所有代码都将上传至:项目代码github地址,喜 ...

  7. [LeetCode] Perfect Rectangle 完美矩形

    Given N axis-aligned rectangles where N > 0, determine if they all together form an exact cover o ...

  8. [LeetCode] Rectangle Area 矩形面积

    Find the total area covered by two rectilinear rectangles in a2D plane. Each rectangle is defined by ...

  9. [LeetCode] Maximal Rectangle 最大矩形

    Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and ...

随机推荐

  1. 必须使用角色管理工具 安装或配置microsoft.net framework 3.5

    windows server 2008安装sql server 2012后报错,要求安装microsoft.net framework 3.5 sp1 但安装时提示,必须使用角色管理工具 安装或配置m ...

  2. blazor wasm访问非本地的restful service

    准备工作 blazor wasm正式版发布了!在尝试使用的过程中,发现几个小坑,跟大家分享一下,希望有所帮助. 我是通过keycloak来保护blazor和service的,如何保护service请参 ...

  3. 量子纠错码——Clifford group

    Clifford code Clifford group是什么? 简单的公式来表达,就是 \(Cl_{n}=\left\{U: U P_{n} U^{\dagger} \in P_{n}\right\ ...

  4. thymeleaf将对象ModelList数据抛到HTML页面

  5. linux高级管理第十二章--rsync

    实验部分 1.安装rsync 2.配置文件 3.配置密码 4.后续 5.为了测试,创建几个文件 配置实时同步 1.调整inotify内核参数 安装inotify-tools 测试同步 编写脚本 验证 ...

  6. Linux部分场景非常有用的命令集1_chattr&ldd&xargs&screen&ssh&磁盘&du

    这里不做详细说明或截图,仅作为记录和简单说明.注:可能只针对某一命令部分功能,不包含整个功能,若要查看全部请自行查阅文档或help 1.chattr 当某一文件或目录,不想被无意修改或删除(即使roo ...

  7. 【数字图像处理】OpenCV中cv2.imread()与PIL中Image.open()的差别

    cv2.imread()与PIL中Image.open()两个函数都是用来读取图像,但是在使用过程存在一些差别. 1. 首先,从导入库方面看: # opencv-python import cv2 # ...

  8. Java多线程--synchronized的用法

    全面介绍Synchronized:https://blog.csdn.net/luoweifu/article/details/46613015 synchronized class/this/obj ...

  9. 02 . Prometheus告警处理

    Prometheus告警简介 告警能力在Prometheus的架构中被划分成两个独立的部分.如下所示,通过在Prometheus中定义AlertRule(告警规则),Prometheus会周期性的对告 ...

  10. PowerPC-MPC56xx Flash模式启动过程

    https://mp.weixin.qq.com/s/SpBOfzJJ1OizYP-rsLJVqQ   Flash启动模式为最常用模式. 一般情况下,芯片上电之后,从flash起始位置读取第一条指令的 ...