题面

题解

(题目中说的四种摆放方式实际上是分别旋转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形覆盖 - (图论、简单推导)的更多相关文章

  1. [LOJ#2326]「清华集训 2017」简单数据结构

    [LOJ#2326]「清华集训 2017」简单数据结构 试题描述 参加完IOI2018之后就是姚班面试.而你,由于讨厌物理.并且想成为乔布斯一样的创业家,被成功踢回贵系. 转眼,时间的指针被指向201 ...

  2. MyISAM和innoDB对比,覆盖索引简单回顾

    MyISAM Myisam是Mysql的默认存储引擎,当create创建新表时,未指定新表的存储引擎时,默认使用Myisam. 它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以 ...

  3. OI图论 简单学习笔记

    网络流另开了一个专题,所以在这里就不详细叙述了. 图 一般表示为\(G=(V,E)\),V表示点集,E表示边集 定义图G为简单图,当且仅当图G没有重边和自环. 对于图G=(V,E)和图G2=(V2,E ...

  4. 模拟赛38 B. T形覆盖 大模拟

    题目描述 如果玩过俄罗斯方块,应该见过如下图形: 我们称它为一个 \(T\) 形四格拼板 .其中心被标记为\(×\). 小苗画了一个 \(m\) 行 \(n\) 列的长方形网格.行从 \(0\) 至 ...

  5. 剑指offer-矩形覆盖10

    题目描述 我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形.请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? # -*- coding:utf-8 -*- class S ...

  6. JZ-010-矩形覆盖

    矩形覆盖 题目描述 我们可以用21的小矩形横着或者竖着去覆盖更大的矩形.请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? 题目链接: 矩形覆盖 代码 /** * 标题:矩形覆盖 ...

  7. 剑指offer-矩形覆盖-斐波那契数列(递归,递推)

    class Solution { public: int rectCover(int number) { if(number==0 || number==1||number==2) return nu ...

  8. 贝塞尔曲线.简单推导与用opengl实现动态画出。

    在opengl中,我们可以用少许的参数来描述一个曲线,其中贝塞尔曲线算是一种很常见的曲线控制方法,我们先来看维基百科里对贝塞尔曲线的说明: 线性贝塞尔曲线 给定点P0.P1,线性贝塞尔曲线只是一条两点 ...

  9. Python学习3——Python的简单推导

    列表推导是一种从其他列表创建列表的方式,类似于数学中的集合推导,列表推导的工作原理非常简单,类似于for循环.(以下代码均在IDLE实现) 最简单的列表推导: >>>[x*x for ...

随机推荐

  1. rpc的正确打开方式|读懂Go原生net/rpc包

    前言 最近在阅读字节跳动开源RPC框架Kitex的源码,分析了如何借助命令行,由一个IDL文件,生成client和server的脚手架代码,也分析了Kitex的日志组件klog.当然Kitex还有许多 ...

  2. 技术分享 | 一步一步学测试平台开发-Vue restful请求

    本文节选自霍格沃兹测试学院内部教材 一般在构建应用时需要访问后端的 API 接口获取后端数据并展示.做这件事的方法有很多种(比如 axios,vue-resource,fetch-jsonp),使用 ...

  3. 万字剖析Ribbon核心组件以及运行原理

    大家好,本文我将继续来剖析SpringCloud中负载均衡组件Ribbon的源码.本来我是打算接着OpenFeign动态代理生成文章直接讲Feign是如何整合Ribbon的,但是文章写了一半发现,如果 ...

  4. 关于Vue的几个实用知识点(持续更新中……)

    前言 排名不分先后,按自己习惯来的. 一.provide.inject 高级组件 总述: provide在父组件中定义,inject 在子孙组件中定义. provide:选项应该是一个对象或返回一个对 ...

  5. BluePrism手把手教程2.0 创建流程

    2.0.1 创建流程 2.0.2 设置流程名称 2.0.3 添加流程说明 2.0.4 添加流程成功 2.0.4 打开新建的流程 RPA行业微信交流群,欢迎大家扫码加入一起交流,此群用于RPA行业技术. ...

  6. RPA-UiPath视频教程1

    UiPath下载.安装.激活.第一个案例Helloworld!.参数类型.变量的介绍和使用 https://www.bilibili.com/video/av92816532 RPA直播公开课2020 ...

  7. bat实现删除BCUnrar.dll实现无限使用

    删除项目:计算机\HKEY_CURRENT_USER\Software\Scooter Software\Beyond Compare 4下的CacheId 项可以实现Beyond Compare 4 ...

  8. colab运行.py文件

    !python split_data.py

  9. vue在Docker上运行

    Dockerfile # 设置基础镜像 FROM nginx:latest # 定义作者 MAINTAINER test # 将dist文件中的内容复制到 /etc/nginx/html/ 这个目录下 ...

  10. 通过Go语言创建CA与签发证书

    本篇文章中,将描述如何使用go创建CA,并使用CA签署证书.在使用openssl创建证书时,遵循的步骤是 创建秘钥 > 创建CA > 生成要颁发证书的秘钥 > 使用CA签发证书.这种 ...