这个码量绝对是业界大毒瘤......

300行,6.5k,烦的要死......

题意:给你一个网格图,里面有0或1。你需要把一些0换成1使得存在某两个0不四联通。输出最小的换的数量。无解-1。

n,m<=1e9,网格中1的数量<=1e5,多组数据。

首先我们发现,最多只要2就行了(围住一个角落),所以答案是[-1,2]中的整数。

然后考虑何时为-1:0的数目小于2或等于2且相连。

何时为0:图初始就不连通。

何时为1:图中存在割点。

除此之外就是2了。

然后发现图很大,c很小,考虑离散化。

然后发现我们只要把每个1周围的点提取出来即可。

提取3×3是错误的,有个众人皆知的样例:

0 0 0

0 0 0

0 1

显然提取之后会有一个割点在原图正中间,但是实际上它并不是割点。

然后我们暴力一点,提取5×5即可......

算法流程:提取点,编号。然后判断联通性。然后做tarjan,判断割点。

然后又有好多坑点...比如割点必须在某个1的周围3×3区域(易证),如果忽视这个就会出现一种毒瘤情况:

1 0 0 0 0 0

0 0 0 0 0 0

0 0 0 0 0 0

0 0 0 0 0 0

0 0 0 0 0 1

可以发现在奇怪的地方出现了割点...

然后还要特判,(n - 1)(m - 1) = 0的时候答案不可能为2。

然后怒写一天半终于对了,又发现map太慢跑不过......手写hash。

终于A了....然后uoj日常97分......

[update]如何判断答案为0:对那些提取出来的非关键点进行并查集。然后枚举每个关键点连通块,如果某个关键点连通块连着两个并查集,答案为0。

 #include <cstdio>
#include <algorithm>
#include <cstring> inline void read(int &x) {
x = ;
char c = getchar();
while(c < '' || c > '') {
c = getchar();
}
while(c >= '' && c <= '') {
x = (x << ) + (x << ) + c - ;
c = getchar();
}
return;
} const int N = ;
const int dx[] = {, , , -};
const int dy[] = {, , -, }; const int MO = , B = ;
struct POS {
int x, y, h;
POS(int xx = , int yy = ) {
x = xx;
y = yy;
h = (1ll * x * B + y) % MO;
if(h < ) {
h += MO;
}
}
inline bool operator ==(const POS &d) const {
return x == d.x && y == d.y;
}
};
struct Node {
int nex, val;
POS p;
}node[N * ]; int top;
struct MAP {
int e[MO];
inline void insert(const POS &d, const int &a) {
node[++top].val = a;
node[top].nex = e[d.h];
node[top].p = d;
e[d.h] = top;
return;
}
inline int find(const POS &d) { // if not exist return 0
for(int i = e[d.h]; i; i = node[i].nex) {
if(node[i].p == d) {
return node[i].val;
}
}
return ;
}
inline void clear() {
memset(e, , sizeof(e));
return;
}
}mp, use; int n, m, c, xi[N], yi[N], tot, num, root;
int dfn[N * ], low[N * ], vis[N * ];
bool cut[N * ], vis_c[N], OK; inline void np(int x, int y) {
if(!mp.find(POS(x, y)) && !use.find(POS(x, y))) {
mp.insert(POS(x, y), ++tot);
}
return;
} inline int get(int x, int y) {
return mp.find(POS(x, y));
} void tarjan(int s, int x, int y) {
dfn[s] = low[s] = ++num;
int temp = ;
for(int i = ; i < ; i++) {
int t = get(x + dx[i], y + dy[i]);
if(!t) {
continue;
}
if(!dfn[t]) {
tarjan(t, x + dx[i], y + dy[i]);
low[s] = std::min(low[s], low[t]);
if(low[t] >= dfn[s]) {
temp++;
}
}
else {
low[s] = std::min(low[s], dfn[t]);
}
}
if(temp >= || (temp == && s != root)) {
cut[s] = ;
}
return;
} void DFS_1(int s, int x, int y, int temp) {
vis[s] = temp;
for(int i = ;i < ; i++) {
int t = get(x + dx[i], y + dy[i]);
if(!t) {
continue;
}
if(!vis[t]) {
DFS_1(t, x + dx[i], y + dy[i], temp);
}
}
return;
} bool fd;
int number; bool DFS_2(int s, int x, int y) {
vis_c[s] = ;
for(int i = ; i < ; i++) {
if(use.find(POS(x + dx[i], y + dy[i]))) {
int ed = use.find(POS(x + dx[i], y + dy[i]));
if(vis_c[ed]) {
continue;
}
int t = DFS_2(ed, x + dx[i], y + dy[i]);
if(!t) {
return ;
}
}
else if(get(x + dx[i], y + dy[i])) {
if(!fd) {
number = vis[get(x + dx[i], y + dy[i])];
fd = ;
}
else if(number != vis[get(x + dx[i], y + dy[i])]) {
OK = ;
return ;
}
}
}
return ;
} inline bool check() {
OK = ;
int temp = ;
for(int i = ; i <= c; i++) {
for(int x = xi[i] - ; x <= xi[i] + ; x++) {
for(int y = yi[i] - ; y <= yi[i] + ; y++) {
if(vis_c[i]) {
continue;
}
if(mp.find(POS(x, y)) && !vis[get(x, y)]) {
++temp;
DFS_1(get(x, y), x, y, temp);
goto f1;
}
}
}
f1:
if(!vis_c[i]) {
fd = ;
DFS_2(i, xi[i], yi[i]);
}
if(!OK) {
break;
}
}
return !OK;
} inline int solve() {
read(n);
read(m);
read(c);
if(!c) {
if(n == && m == ) {
return -;
}
if(n == || m == ) {
if(n == || m == ) {
return -;
}
return ;
}
return ;
}
for(int i = ; i <= c; i++) {
read(xi[i]);
read(yi[i]);
use.insert(POS(xi[i], yi[i]), i);
}
if(c + >= 1ll * n * m) {
return -;
}
for(int i = ; i <= c; i++) {
for(int x = xi[i] - ; x <= xi[i] + ; x++) {
for(int y = yi[i] - ; y <= yi[i] + ; y++) {
if(x > && y > && x <= n && y <= m && (x != xi[i] || y != yi[i])) {
np(x, y);
}
}
}
}
if(check()) {
return ;
}
if(c + == 1ll * n * m) {
return -;
}
if(m == || n == ) {
return ;
}
for(int i = ; i <= c; i++) {
for(int x = xi[i] - ; x <= xi[i] + ; x++) {
for(int y = yi[i] - ; y <= yi[i] + ; y++) {
if(!use.find(POS(x, y))) {
root = get(x, y);
if(dfn[root]) {
continue;
}
tarjan(root, x, y);
}
}
}
} for(int i = ; i <= c; i++) {
for(int x = xi[i] - ; x <= xi[i] + ; x++) {
for(int y = yi[i] - ; y <= yi[i] + ; y++) {
int s = get(x, y);
if(cut[s]) {
return ;
}
}
}
}
return ;
} inline void clear() {
mp.clear();
use.clear();
memset(dfn + , , tot * sizeof(int));
memset(low + , , tot * sizeof(int));
memset(cut + , , tot * sizeof(bool));
memset(vis + , , tot * sizeof(int));
memset(vis_c + , , c * sizeof(bool));
tot = ;
num = ;
top = ;
return;
} int main() {
int T;
read(T);
while(T--) {
printf("%d\n", solve());
if(T) {
clear();
}
}
return ;
}

AC代码

找个时间在uoj上A一A。

洛谷P1173 [NOI2016]网格的更多相关文章

  1. 洛谷P1712 [NOI2016]区间 尺取法+线段树+离散化

    洛谷P1712 [NOI2016]区间 noi2016第一题(大概是签到题吧,可我还是不会) 链接在这里 题面可以看链接: 先看题意 这么大的l,r,先来个离散化 很容易,我们可以想到一个结论 假设一 ...

  2. 并不对劲的bzoj4651:loj2084:uoj220:p1173:[NOI2016]网格

    题目大意 有一个\(n*m\)(\(n,m\leq10^9\))的网格,每个格子是空地或障碍(\(障碍数\leq10^5\)) 定义两块空地连通,当且仅当它们是"相邻的两块空地"或 ...

  3. uoj233/BZOJ4654/洛谷P1721 [Noi2016]国王饮水记 【dp + 斜率优化】

    题目链接 uoj233 题解 下面不加证明地给出几个性质: 小于\(h[1]\)的城市一定是没用的 任何城市联通包含\(1\)且只和\(1\)联通一次 联通顺序从小到大最优 单个联通比多个一起联通要优 ...

  4. bzoj 4650(洛谷 1117) [Noi2016]优秀的拆分——枚举长度的关键点+后缀数组

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4650 https://www.luogu.org/problemnew/show/P1117 ...

  5. 洛谷P1587 [NOI2016]循环之美

    传送门 不会,先坑着 https://kelin.blog.luogu.org/solution-p1587 //minamoto #include<cstdio> #include< ...

  6. 洛谷 P1712 [NOI2016]区间(线段树)

    传送门 考虑将所有的区间按长度排序 考虑怎么判断点被多少区间覆盖,这个可以离散化之后用一棵权值线段树来搞 然后维护两个指针$l,r$,当被覆盖次数最多的点的覆盖次数小于$m$时不断右移$r$,在覆盖次 ...

  7. [洛谷P1712] NOI2016 区间

    问题描述 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一 ...

  8. 洛谷$P1712\ [NOI2016]$区间 线段树

    正解:线段树 解题报告: 传送门$QwQ$ $umm$很久以前做的了来补个题解$QwQ$ 考虑给每个区间按权值($r-l$从大往小排序,依次加入,然后考虑如果有一个位置被覆盖次数等于$m$了就可以把权 ...

  9. [Noi2016]区间 BZOJ4653 洛谷P1712 Loj#2086

    额... 首先,看到这道题,第一想法就是二分答案+线段树... 兴高采烈的认为我一定能AC,之后发现n是500000... nlog^2=80%,亲测可过... 由于答案是求满足题意的最大长度-最小长 ...

随机推荐

  1. CBV源码分析+APIVIew源码分析

    {drf,resful,apiview,序列化组件,视图组件,认证组件,权限组件,频率组件,解析器,分页器,响应器,URL控制器,版本控制} 一.CBV源码分析准备工作: 新建一个Django项目 写 ...

  2. Android——MaterialDesign之一Toolbar

    Toolbar 由于ActionBar设计原因只能存在活动的顶部,从而不能实现MaterialDesign的效果,现在推荐使用Toolbar,继承Actionbar,但是比起它更加的灵活. 设置主题: ...

  3. python之路-字符串

    一.类型转换 a = 10 print(type(a)) # <class 'int'> d = str(a) # 把数字转换成str print(type(d)) # <class ...

  4. Django进阶知识

    drf学习之Django进阶点 一.Django migrations原理 1.makemigrattions: 相当于在每个app下的migrations文件夹下生成一个py脚本文件用于创建表或则修 ...

  5. C# 将当前应用程序写入到注册表开机启动项中

    在使用C#进行应用程序的开发过程中,经常有一个需求就是让应用程序开机后自动启动,这个是一个很常见的需求,最常规的做法(这里以Win7操作系统为例),打开:开始=>所有程序=>启动文件夹(路 ...

  6. python数据结构与算法第五天【顺序表】

    1.列表存储的两种方式 (1)元素内置方式 采用元素内置的方式只能存放同类型元素的数据类型,例如列表中的元素都为整形,元素类型相同,每个元素存放的地址空间大小也相同,则列表中每个元素都是顺序存放的 ( ...

  7. 老男孩python学习自修第十天【三元表达式与lambda表达式】

    例如: 1.使用三元表达式给变量赋值 result = '空' if x == None else x 2.使用lambda定义函数 add = lambda x, y: x+y

  8. Java的hashCode和equals方法

    当然健壮的代码,两个都重写那是最好. 用不到hashCode的, 也没有必要重写hashCode. 个人感觉. 哈希机制的Java集合类,例如 Hashtable, HashMap, HashSet ...

  9. Vue插件plugins的基本操作

    前面的话 本文将详细介绍Vue插件plugins的基本操作 开发插件 插件通常会为 Vue 添加全局功能.插件的范围没有限制——一般有下面几种: 1.添加全局方法或者属性,如: vue-custom- ...

  10. THEKEY

    追溯历史,有据可查的身份证明是中国隋唐时期的“鱼符”,只为达官贵人证明身份等级,后演变为“龟符”.“牙牌”.“腰牌”等,直至近现代时期才出现真正意义的身份证,增加了个人信息描述.由于过去的生产力水平较 ...