「BZOJ 5010」「FJOI 2017」矩阵填数「状压DP」
题意
你有一个\(h\times w\)的棋盘,你需要在每个格子里填\([1, m]\)中的某个整数,且满足\(n\)个矩形限制:矩形的最大值为某定值。求方案数\(\bmod 10^9+7\)
\(h, w, m\leq 10^4,n\leq 10\)
题解
首先来考虑单独的一个矩形限制怎么做。假设矩形面积为\(s\),最大值为\(v\)
易得答案是\(v^{s}-(v-1)^{s}\),意思就是每个数随便选,然后减去所有数\(<v\)的方案
现在考虑\(n\)个限制,实际上把棋盘分成了\(O(2^n)\)个部分。注意这里,包含两个格子的矩形集合相同,则两个格子算一个部分,并不是按四连通来定义的。
那我们对于每一部分,假设有若干的矩形包含它,那这个部分能放的最大值是所有包含它的矩形的限制的最小值。
这就是说一个矩形限制,被分成若干部分,这些部分必须有一个取到最大值,就考虑状压DP
\(dp[i][S]\)表示前i个部分,\(S\)集合里的矩形已经被满足,方案数是多少。
设当前部分\(i\)的大小为\(sz[i]\),包含这个部分的矩形中限制恰好为当前部分限制\(v\)的矩形集合为\(cov[i]\),当前部分的限制为\(v\)
要取到最大值:
\(dp[i + 1][j] += dp[i][j]\times (v-1)^{sz[i]}\)
不取最大值:
\(dp[i + 1][j | cov[i]] += dp[i][j](v^{sz[i]} - (v-1)^{sz[i]})\)
然后就很暴力地做完了。
比较新奇的是每个部分实际面积的求法(我没见过),对于每个交集,原大小减去子集的实际面积大小
代码更加直观地展现我在说什么。
#include <algorithm>
#include <cstdio>
using namespace std;
const int mo = 1e9 + 7;
const int N = 10;
struct matrix {
int x1, y1, x2, y2, d;
void operator &= (const matrix &b) {
d = min(d, b.d);
x1 = max(x1, b.x1);
y1 = max(y1, b.y1);
x2 = min(x2, b.x2);
y2 = min(y2, b.y2);
if(x1 > x2 || y1 > y2) d = -1;
}
} a[N], b[1 << N];
int h, w, m, n, t, dp[1100][1 << N], cov[1 << N], sz[1 << N], st[1 << N], sum;
void dfs(int u, matrix mat, int s) {
if(u == n) { b[s] = mat; return ; }
dfs(u + 1, mat, s); mat &= a[u];
dfs(u + 1, mat, s | (1 << u));
}
int qpow(int a, int b) {
int ans = 1;
for(; b >= 1; b >>= 1, a = 1ll * a * a % mo)
if(b & 1) ans = 1ll * ans * a % mo;
return ans;
}
void chk(int &u) { u >= mo ? u -= mo : 0; }
int main() {
int te; scanf("%d", &te);
while(te --) {
scanf("%d%d%d%d", &h, &w, &m, &n);
for(int i = 0; i < n; i ++)
scanf("%d%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2, &a[i].d);
dfs(0, (matrix) {1, 1, h, w, m}, 0);
for(int i = 0; i < (1 << n); i ++) sz[i] = b[i].d == -1 ? 0 : (b[i].x2 - b[i].x1 + 1) * (b[i].y2 - b[i].y1 + 1);
for(int i = (1 << n) - 1; i >= 1; i --) {
sz[0] -= sz[i];
for(int j = (i - 1) & i; j; j = (j - 1) & i) sz[j] -= sz[i];
}
t = 0;
for(int i = 1; i < (1 << n); i ++) if(sz[i] >= 1) {
cov[i] = 0;
for(int j = 0; j < n; j ++) if((i >> j & 1) && a[j].d == b[i].d) cov[i] |= 1 << j;
}
for(int i = 1; i < (1 << n); i ++) if(sz[i] >= 1) st[++ t] = i;
for(int i = 0; i <= t; i ++) fill(dp[i], dp[i] + (1 << n), 0);
dp[0][0] = 1;
for(int i = 0; i < t; i ++) {
int nd = sz[st[i + 1]], up = b[st[i + 1]].d;
int t1 = qpow(up, nd), t2 = qpow(up - 1, nd);
for(int s = 0; s < (1 << n); s ++) if(dp[i][s]) {
chk(dp[i + 1][s] += 1ll * dp[i][s] * t2 % mo);
chk(dp[i + 1][s | cov[st[i + 1]]] += 1ll * dp[i][s] * (t1 - t2 + mo) % mo);
}
}
printf("%d\n", 1ll * qpow(m, sz[0]) * dp[t][(1 << n) - 1] % mo);
}
return 0;
}
「BZOJ 5010」「FJOI 2017」矩阵填数「状压DP」的更多相关文章
- 「状压DP」「暴力搜索」排列perm
「状压DP」「暴力搜索」排列 题目描述: 题目描述 给一个数字串 s 和正整数 d, 统计 sss 有多少种不同的排列能被 d 整除(可以有前导 0).例如 123434 有 90 种排列能被 2 整 ...
- 「BZOJ 4565」「HAOI 2016」字符合并「区间状压DP」
题意 给一个长度为\(n(\leq 300)\)的\(01\)串,每次可以把\(k(\leq 8)\)个相邻字符合并,得到新字符和一定分数,最大化最后的得分 题解 考虑设计dp:\(dp[S][i][ ...
- 「BZOJ 5161」最长上升子序列「状压DP」
题意 求一个\(1\sim n\)的排列LIS的期望长度,\(n\leq 28\) 题解 考虑朴素的LIS:\(f[i] = min(f[j]) + 1\) 记\(mx[i]\)为\(f\)的前缀最大 ...
- ☆ [POJ2411] Mondriaan's Dream 「状压DP」
传送门 >Here< 题意:用1*2的砖块铺满n*m的地板有几种方案 思路分析 状压经典题! 我们以$f[i][j]$作为状态,表示第i行之前全部填完并且第i行状态为j(状压)时的方案数. ...
- 「CF744C」Hongcow Buys a Deck of Cards「状压 DP」
题意 你有\(n\)个物品,物品和硬币有\(A\),\(B\)两种类型,假设你有\(M\)个\(A\)物品和\(N\)个\(B\)物品 每一轮你可以选择获得\(A, B\)硬币各\(1\)个,或者(硬 ...
- BZOJ 4006 Luogu P3264 [JLOI2015]管道连接 (斯坦纳树、状压DP)
题目链接: (bzoj)https://www.lydsy.com/JudgeOnline/problem.php?id=4006 (luogu)https://www.luogu.org/probl ...
- BZOJ 2734 洛谷 3226 [HNOI2012]集合选数【状压DP】【思维题】
[题解] 思维题,看了别人的博客才会写. 写出这样的矩阵: 1,3,9,... 2,6,18,... 4,12.36,... 8,24,72,... 我们要做的就是从矩阵中选出一些数字,但是不能选相邻 ...
- [BZOJ 1072] [SCOI2007] 排列perm 【状压DP】
题目链接:BZOJ 1072 这道题使用 C++ STL 的 next_permutation() 函数直接暴力就可以AC .(使用 Set 判断是否重复) 代码如下: #include <io ...
- BZOJ.4145.[AMPPZ2014]The Prices(状压DP)
BZOJ 比较裸的状压DP. 刚开始写麻烦惹... \(f[i][s]\)表示考虑了前\(i\)家商店,所买物品状态为\(s\)的最小花费. 可以写求一遍一定去\(i\)商店的\(f[i]\)(\(f ...
随机推荐
- pb菜单详解和MDI
菜单条-MenuBar.菜单项-MenuItem.级联菜单(子菜单)-SubMenu 菜单项(MenuItem)是菜单中最基本的元素,只要有文字内容的就是菜单项.菜单条(MenuBar)是菜单中级别最 ...
- 调研task_struct结构体
进程的描述PCB task_struct——PCB的一种,在linux中描述进程的结构体叫做task_struct. task_struct内容分类: 标识符:描述本进程的唯一标识符,用来区别其他进程 ...
- Tag Helper1
Tag Helpers是服务器段的C#代码,在Razor文件里,参与到创建和渲染HTML元素的过程 和HTML Helpers类似 跟HTML的命名规范一致 内置了很多Tag Helpers也可以自定 ...
- Activiti使用总结
工作流在我们日常的工作中用得可谓相当普及,尤其在企业内部管理系统,如考勤.财务.合同等系统中更是离不开它.在我们金融科技领域,工作流主要用于贷款审批.风控审核等环节.早期工作流在企业信息化发挥着很重要 ...
- Xcode8警告⚠️ Empty paragraph passed to '@xxx' command
问题 Xcode8升级后,之前添加的注释会有很多警告 解决方法 基础知识,就是在编译选项中,添加警告屏蔽 解决步骤 显示警告信息 显示警告信息.png 查看警告类型 查看警告类型.png 屏蔽警告 W ...
- ASE19团队项目alpha阶段model组 scrum9 记录
本次会议于11月13日,19时整在微软北京西二号楼sky garden召开,持续7分钟. 与会人员:Jiyan He, Kun Yan, Lei Chai, Linfeng Qi, Xueqing W ...
- DevExpress VCL 19.2.3 Skin找不到皮肤
The location where the DX Designtime Loader is looking for the skin packages has changed again ! For ...
- python实现IP地址转换为32位二进制
python实现IP地址转换为32位二进制 #!/usr/bin/env python # -*- coding:utf-8 -*- class IpAddrConverter(object): de ...
- 树的总结(遍历,BST,AVL原型,堆,练习题)
目录 树 一.抽象数据类型 二.二叉树的性质 三.二叉树的遍历 三.活用树的遍历 四.BST树 五.AVL树 六.BST树和AVL树练习 七.堆 树 @ 一.抽象数据类型 1.顺序存储 使用数组存储 ...
- fixed固定元素
1.css <style type="text/css"> .elementFixed{ position: fixed; top:0; } </style> ...