【LOJ#3197】【eJOI2019】T形覆盖 - (图论、简单推导)
题面
题解
(题目中说的四种摆放方式实际上是分别旋转0°,90°,180°,270°后的图形)
题目中关于摆放方式的描述听起来很臭,我们把它转换一下,每个拼版先覆盖“上下左右中”五个格子,然后再在四个相邻格子中减去一个。
那么我们先把每个拼版所在“十字”涂了,然后把有重复涂过的格子当成边,把拼版们通过这些边连起来,成为许多个连通块,不同连通块之间肯定是互不干扰的,可以独立计算贡献。
如下图(我用颜色的中和表示被涂多次,很好理解吧),A、B、C是连通块,注意,D不是连通块,这也是遍历的时候需要注意的情况。
对于每个连通块,里面都会有至少一个的被涂了大于等于两遍的格子,如果有被涂了三遍甚至四遍的格子,那么直接输出No,这比较显然,自己想想就知道了。
由于接下来每个拼版要在相邻的格子中减去一个,即让相邻的一个格子被涂次数-1,所以把每个连通块被涂多次的格子数数出来,记为 E(其实就是边数),把拼版数记为 N(其实就是点数),我们会发现以下结论:
- 若 E > N ,由于一个点可以使 E 减 1 ,N 个就可以减 N,所以 E - N > 0 意味着什么?无论如何连通块内都会存在被重复覆盖的格子,即无解,输出No
- 若 E == N,即 E - N == 0,刚好可以把重复格子清完,即为环或基环树,就如上图的A、B、C(A、B也是刚好相等的!也就是说“两个点可以有重边”),也就是说答案就是这个连通块覆盖的所有格子中的数的和,不多不少
- 若 E < N,此时只可能是 E == N-1,即为一个树状图,此时不仅可以把被涂的多余层全消完(此多余层可以为拼版中心格子),还可以多消一个非拼版中心的格子(包括先前被涂多次,后来被消成单层的格子),然后,任何连通块内这样的格子都可以被选择作为多消掉的一个,为什么呢?因为它是树形结构,所以相邻两块拼版最多有一个格子重复覆盖(不然超过一个就有环了嘛),把任意一个可消的格子选择后,所在的一个或两个拼版状态就确定了,就可以顺推出整棵树的每个拼版的状态,而且可以证明是一定有解的!……
……只不过你得特殊处理一下贴墙的情况,不过不影响结论。
至于怎么DFS,只用模拟走边,跑图,然后计算覆盖的格子的信息就行了。如果是 E < N ,就选一个权值最小的可选格子令ans减去它即可。注意ans是最早先不重复地把覆盖的所有格子的数加进去了的。
复杂度O(nm)
CODE
如果测大数据时运行爆了,不用怕,开无限栈应该就解决了
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 1000005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) ((-x) & (x))
LL read() {
LL f = 1,x = 0;char s = getchar();
while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
return f * x;
}
const int MOD = 1000000007;
int n,m,i,j,s,o,k;
vector<int> a[MAXN];
int c[MAXN],d[MAXN];
bool f[MAXN];
bool Out(int x,int y) {return x < 1 || y < 1 || x > n || y > m;}
int cg(int x,int y) {return max(0,min((x-1)*m+y,n*m+1));}
struct it{
int x,y;
}p[MAXN];
int md,cn,flag,mi,cnt;
void dfs(int x,int y) {
if(!flag || Out(x,y) || f[cg(x,y)] || (!c[cg(x,y)])) return ;
f[cg(x,y)] = 1;
if(c[cg(x,y)] > 2) {
flag = 0; return ;
}
if(c[cg(x,y)] == 2) md ++;
if(d[cg(x,y)]) {
cn ++;
if(Out(x-1,y)) md ++;
if(Out(x+1,y)) md ++;
if(Out(x,y+1)) md ++;
if(Out(x,y-1)) md ++;
}
else mi = min(mi,a[x][y]);
if(c[cg(x,y)] == 2) {
if(!Out(x-1,y) && d[cg(x-1,y)]) dfs(x-1,y);
if(!Out(x+1,y) && d[cg(x+1,y)]) dfs(x+1,y);
if(!Out(x,y-1) && d[cg(x,y-1)]) dfs(x,y-1);
if(!Out(x,y+1) && d[cg(x,y+1)]) dfs(x,y+1);
}
else if(d[cg(x,y)]) {
dfs(x-1,y);dfs(x+1,y);
dfs(x,y-1);dfs(x,y+1);
}
return ;
}
int solve(int x,int y) {
mi = 0x7f7f7f7f;
md = cn = 0;
dfs(x,y);
if(md > cn || !flag) {
flag = 0;return 0;
}
if(md == cn) return 0;
if(md < cn) return mi;
return 0;
}
int main() {
freopen("t-covering.in","r",stdin);
freopen("t-covering.out","w",stdout);
n = read(); m = read();
for(int i = 1;i <= n;i ++) {
a[i].push_back(0);
for(int j = 1;j <= m;j ++) {
a[i].push_back(read());
}
}
k = read();
for(int i = 1;i <= k;i ++) {
s = p[i].x = read()+1;o = p[i].y = read()+1;
c[cg(s,o)] ++; d[cg(s,o)] ++;
if(s > 1) c[cg(s-1,o)] ++;
if(s < n) c[cg(s+1,o)] ++;
if(o > 1) c[cg(s,o-1)] ++;
if(o < m) c[cg(s,o+1)] ++;
}
LL ans = 0;
flag = 1;
for(int i = 1;i <= n;i ++) {
for(int j = 1;j <= m;j ++) {
if(c[cg(i,j)]) {
ans += a[i][j];
}
if(d[cg(i,j)] && !f[cg(i,j)]) {
ans -= solve(i,j);
}
if(!flag) {
printf("No\n");
return 0;
}
}
}
printf("%lld\n",ans);
return 0;
}
【LOJ#3197】【eJOI2019】T形覆盖 - (图论、简单推导)的更多相关文章
- [LOJ#2326]「清华集训 2017」简单数据结构
[LOJ#2326]「清华集训 2017」简单数据结构 试题描述 参加完IOI2018之后就是姚班面试.而你,由于讨厌物理.并且想成为乔布斯一样的创业家,被成功踢回贵系. 转眼,时间的指针被指向201 ...
- MyISAM和innoDB对比,覆盖索引简单回顾
MyISAM Myisam是Mysql的默认存储引擎,当create创建新表时,未指定新表的存储引擎时,默认使用Myisam. 它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以 ...
- OI图论 简单学习笔记
网络流另开了一个专题,所以在这里就不详细叙述了. 图 一般表示为\(G=(V,E)\),V表示点集,E表示边集 定义图G为简单图,当且仅当图G没有重边和自环. 对于图G=(V,E)和图G2=(V2,E ...
- 模拟赛38 B. T形覆盖 大模拟
题目描述 如果玩过俄罗斯方块,应该见过如下图形: 我们称它为一个 \(T\) 形四格拼板 .其中心被标记为\(×\). 小苗画了一个 \(m\) 行 \(n\) 列的长方形网格.行从 \(0\) 至 ...
- 剑指offer-矩形覆盖10
题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? # -*- coding:utf-8 -*- class S ...
- JZ-010-矩形覆盖
矩形覆盖 题目描述 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形.请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 题目链接: 矩形覆盖 代码 /** * 标题:矩形覆盖 ...
- 剑指offer-矩形覆盖-斐波那契数列(递归,递推)
class Solution { public: int rectCover(int number) { if(number==0 || number==1||number==2) return nu ...
- 贝塞尔曲线.简单推导与用opengl实现动态画出。
在opengl中,我们可以用少许的参数来描述一个曲线,其中贝塞尔曲线算是一种很常见的曲线控制方法,我们先来看维基百科里对贝塞尔曲线的说明: 线性贝塞尔曲线 给定点P0.P1,线性贝塞尔曲线只是一条两点 ...
- Python学习3——Python的简单推导
列表推导是一种从其他列表创建列表的方式,类似于数学中的集合推导,列表推导的工作原理非常简单,类似于for循环.(以下代码均在IDLE实现) 最简单的列表推导: >>>[x*x for ...
随机推荐
- React中http-proxy-middleware代理使用
React项目npm run start启动本地服务后浏览器访问http://localhost:3000 start命令也可自定义port指定本地运行端口(eg: PORT=3002) 对于单点登录 ...
- Linux系列之linux访问windows文件
Linux永久挂载windows共享文件 Linux系统必须安装samba-client Linux服务器必须能访问到Windows的共享文件服务的(445端口) 1.Windows共享文件 2.测试 ...
- [pwn基础]Pwntools学习
目录 [pwn基础]Pwntools学习 Pwntools介绍 Pwntools安装 Pwntools常用模块和函数 pwnlib.tubes模块学习 tubes.process pwnlib.con ...
- 使用Node.js还可以发邮件
前言 今天,我们给大家开发一个小效果.篇幅比较短,主要给大家展示效果.实战 首先我们初始化一个Node项目 npm init -y 创建一个app.js文件 'use strict'; const n ...
- 2分钟实现一个Vue实时直播系统
前言 我们在不敲代码的时候可能会去看游戏直播,那么是前台怎么实现的呢?下面我们来讲一下.第一步,购买云直播服务 首先,你必须去阿里云或者腾讯云注册一个直播服务.也花不了几个钱,练手的话,几十块钱就够了 ...
- SAP 实例 3 Context Menus
REPORT demo_dynpro_context_menu. DATA: field1 TYPE i VALUE 10, field2 TYPE p DECIMALS 4. DATA: prog ...
- 使用c++爬取股市数据,获取最新行情
最近自己动手写个小软件(界面原生态,还没来得及加样式哈).每天看看潜力股懒人做法,不介意推荐.资源有限,只能观察一下低价股,分析一下运动规律,什么时候拉升,惯性如何 主要功能:读取网络数据:保存本地数 ...
- Eclipse拷贝动态的web工程
1.选中需要拷贝的工程,CTRL+C,然后CTRL+V 2.在web动态工程中,还需要选中新拷贝工程,右键选中properties,然后搜索web,--->Web Project Setttin ...
- Tomcat 安装及配置,创建动态的web工程
Tomcat可以认为是对Servlet标准的实现,是一个具体的Servlet容器. 1) 将Tomcat的安装包解压到磁盘的任意位(非中文无空格) 2) Tomcat服务的 ...
- FileNameFilter过滤器的使用和Lambda优化程序--IO概述(概念&分类)
FileNameFilter过滤器的使用和Lambda优化程序 public class Demo02Filter { public static void main(String[] args) { ...