[UOJ#220][BZOJ4651][Noi2016]网格

试题描述

跳蚤国王和蛐蛐国王在玩一个游戏。
他们在一个 n 行 m 列的网格上排兵布阵。其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐,其余的格子中,每个格子有一只跳蚤。
我们称占据的格子有公共边的两只跳蚤是相邻的。
我们称两只跳蚤是连通的,当且仅当这两只跳蚤相邻,或存在另一只跳蚤与这两只跳蚤都连通。
现在,蛐蛐国王希望,将某些(0 个,1 个或多个)跳蚤替换成蛐蛐,使得在此之后存在至少两只跳蚤不连通。
例如:我们用图表示一只跳蚤,用图表示一只蛐蛐,那么图 1 描述了一个 n=4,m=4,c=2的情况。
这种情况下蛐蛐国王可以通过将第 2 行第 2 列,和第 3 行第 3 列的两只跳蚤替换为蛐蛐,从而达成他的希望,如图 2 所示。并且,不存在更优的方案,但是可能存在其他替换 2 只跳蚤的方案。
你需要首先判断蛐蛐国王的希望能否被达成。如果能够达成,你还需要最小化被替换的跳蚤的个数。
 

输入

每个输入文件包含多组数据。
输入文件的第一行只有一个整数 T,表示数据的组数。保证 1≤T≤20。
接下来依次输入 TT 组数据,每组数据的第一行包含三个整数 n, m, c。
保证1≤n,m≤10^9,0≤c≤min(nm,105)
接下来 c行,每行包含两个整数 x, y表示第 x 行,第 y 列的格子被一个蛐蛐占据(1≤x≤n,1≤y≤m)每一组数据当中,同一个蛐蛐不会被多次描述。
同一行相邻的整数之间由一个空格隔开。
1≤n,m≤10^9, 0≤c≤nm, 1≤x≤n, 1≤y≤m
1≤T≤20。我们记 ∑c为某个测试点中,其 T 组输入数据的所有 c 的总和,∑c≤10^5

输出

对于每一组数据依次输出一行答案。
如果这组数据中,蛐蛐国王的希望不能被达成,输出-1。否则,输出被替换的跳蚤的个数的最小值

输入示例


输出示例


-

数据规模及约定

见“输入

题解

这题数据太强了。。。再加上 UOJ 上 hack 狂魔泛滥。。。调了我一个上午,快吐血了。。。

答案只有 4 种:-1、0、1 和 2。

那么分类讨论即可

ans = -1:nm-k < 2 或 nm - k = 2 且两个空地相邻

ans = 0:不连通

ans = 1:存在割顶

ans = 2:其余情况

那么我们找出所有障碍方块往外扩两圈的空地(一个障碍最多产生 24 个空地),四联通建图即可。

对于 ans = 0 的情况,看每个障碍连通块外面的空地是否联通,注意是障碍连通块,而不是简单地每个障碍考虑一遍就行了,因为我们可以围成一个铁桶(很厚的障碍)把它卡掉,这是在 hack 数据中的第 6 组出现的。

对于 ans = 1 的情况,跑 tarjan 找割顶,注意对于每个割顶需要判断紧贴着它的周围 8 个块有没有超出边界的或是障碍点,如果没有,它就是一个“假割顶”,反例数据就是在 (1, 1) 和 (5, 5) 两个位置放上障碍,这情况是在第 7 个数据中出现的。

此外,今天还培养出一个 hash hack 狂魔 wzj。。。这就是为什么我在程序开头加了一个线性筛。。。

哦对,tarjan 求割顶时注意特判根节点。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
//#include <windows.h>
using namespace std; int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 2400010
#define maxm 19200010
#define MMOD 5000007
#define X 523
#define x first
#define y second
#define pii pair <int, int>
#define mp(x, y) make_pair(x, y)
#define RND (rand() << 15 | rand())
#define LL long long int cp, prime[MMOD], MOD;
bool Vis[MMOD];
void init() {
for(int i = 2; i < MMOD; i++) {
if(!Vis[i]) prime[++cp] = i;
for(int j = 1; i * prime[j] < MMOD && j <= cp; j++) {
Vis[i*prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
return ;
} struct Hash {
int ToT, head[MMOD], nxt[maxn];
pii pos[maxn]; void init() { ToT = 0; memset(head, 0, sizeof(head)); return ; } int Find(pii ps) {
int u = ((LL)ps.x * X + ps.y) % MOD;
for(int e = head[u]; e; e = nxt[e]) if(pos[e] == ps) return e;
return 0;
}
int Find(int x, int y) {
int u = ((LL)x * X + y) % MOD;
for(int e = head[u]; e; e = nxt[e]) if(pos[e] == mp(x, y)) return e;
return 0;
}
void Insert(pii ps) {
if(Find(ps)) return ;
int u = ((LL)ps.x * X + ps.y) % MOD;
nxt[++ToT] = head[u]; pos[ToT] = ps; head[u] = ToT;
return ;
}
void Insert(int x, int y) {
if(Find(x, y)) return ;
int u = ((LL)x * X + y) % MOD;
nxt[++ToT] = head[u]; pos[ToT] = mp(x, y); head[u] = ToT;
return ;
}
} Spc, Obs; pii Near[maxn/24+10][30];
int cntn[maxn/24+10];
int dx[] = {-2, -2, -2, -2, -2, -1, -1, -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, -3, 3, 0, 0},
dy[] = {-2, -1, 0, 1, 2, -2, -1, 0, 1, 2, -2, -1, 1, 2, -2, -1, 0, 1, 2, -2, -1, 0, 1, 2, 0, 0, -3, 3},
Dx[] = {-1, 1, 0, 0},
Dy[] = {0, 0, -1, 1}; struct Graph {
int m, head[maxn], nxt[maxm], to[maxm];
int fa[maxn]; void init() {
m = 0; memset(head, 0, sizeof(head));
for(int i = 1; i <= Spc.ToT; i++) fa[i] = i;
return ;
} int findset(int x){ return x == fa[x] ? x : fa[x] = findset(fa[x]); } void AddEdge(int a, int b) {
// printf("AddEdge(%d, %d)\n", a, b);
to[++m] = b; nxt[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; nxt[m] = head[a]; head[a] = m;
int u = findset(a), v = findset(b);
if(u != v) fa[v] = u;
return ;
}
} G; bool vis[maxn];
int Q[maxn], hd, tl;
bool BFS(int s, int setu, int n, int m) {
hd = tl = 0; Q[++tl] = s; vis[s] = 1;
while(hd < tl) {
int u = Q[++hd]; pii& ps = Obs.pos[u];
for(int i = 1; i <= cntn[u]; i++) if(G.findset(Spc.Find(Near[u][i])) != setu) return 1;
for(int t = 0; t < 4; t++) {
pii tp = mp(ps.x + Dx[t], ps.y + Dy[t]);
if(1 <= tp.x && tp.x <= n && 1 <= tp.y && tp.y <= m) {
int v = Obs.Find(tp);
if(v && !vis[v]) Q[++tl] = v, vis[v] = 1;
}
}
}
return 0;
} int low[maxn], dfn[maxn], clo;
bool iscut[maxn];
void dfs(int u, int fa) {
dfn[u] = ++clo; low[u] = clo;
// printf("__dfs: %d %d\n", u, dfn[u]);
int son = 0;
iscut[u] = 0;
for(int e = G.head[u]; e; e = G.nxt[e]) if(G.to[e] != fa) {
if(!dfn[G.to[e]]) {
dfs(G.to[e], u);
if(low[G.to[e]] >= dfn[u]) iscut[u] = 1;
low[u] = min(low[u], low[G.to[e]]);
son++;
}
else low[u] = min(low[u], dfn[G.to[e]]);
}
// printf("dfs: %d %d %d\n", u, low[u], dfn[u]);
if(!fa && son == 1) iscut[u] = 0;
return ;
} //int Map[1010][1010]; int main() {
// freopen("grid7.in", "r", stdin);
srand(20162523);
init(); int T = read();
while(T--) {
MOD = prime[RND%50000+cp-49999];
Spc.init(); Obs.init(); int n = read(), m = read(), c = read();
for(int i = 1; i <= c; i++) {
int x = read(), y = read();
Obs.Insert(x, y);
// Map[x][y] = 1;
}
if(!c) Obs.Insert(1, 0), Obs.Insert(0, 1);
for(int i = 1; i <= Obs.ToT; i++) {
pii& ps = Obs.pos[i];
cntn[i] = 0;
for(int t = 0; t < (min(n, m) > 1 ? 24 : 28); t++) {
pii tp = mp(ps.x + dx[t], ps.y + dy[t]);
if(1 <= tp.x && tp.x <= n && 1 <= tp.y && tp.y <= m && !Obs.Find(tp)) {
Near[i][++cntn[i]] = tp, Spc.Insert(tp);
// Map[tp.x][tp.y] = 2;
}
}
}
/*SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
if(Map[i][j] == 0) printf("??");
if(Map[i][j] == 1) printf("??");
if(Map[i][j] == 2) {
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE);
printf("??");
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
}
}
putchar('\n');
}*/ if(c >= (LL)n * m - 1){ puts("-1"); continue; } G.init();
for(int i = 1; i <= Spc.ToT; i++) {
pii& ps = Spc.pos[i];
for(int t = 0; t < 4; t++) {
pii tp = mp(ps.x + Dx[t], ps.y + Dy[t]);
if(1 <= tp.x && tp.x <= n && 1 <= tp.y && tp.y <= m && Spc.Find(tp) && Spc.Find(tp) < i)
G.AddEdge(Spc.Find(tp), i);
}
} if(c == (LL)n * m - 2 && G.findset(1) == G.findset(2)){ puts("-1"); continue; } memset(vis, 0, sizeof(vis));
bool is_0 = 0;
for(int i = 1; i <= Obs.ToT; i++) if(!vis[i] && cntn[i]) {
if(BFS(i, G.findset(Spc.Find(Near[i][1])), n, m)){ is_0 = 1; break; }
}
if(is_0){ puts("0"); continue; } memset(dfn, 0, sizeof(dfn)); clo = 0;
bool is_1 = 0;
for(int i = 1; i <= Spc.ToT; i++) if(!dfn[i]) dfs(i, 0);
for(int i = 1; i <= Spc.ToT; i++) if(iscut[i]) {
pii& ps = Spc.pos[i];
for(int t = 0; t < 24; t++) if(max(abs(dx[t]), abs(dy[t])) < 2) {
pii tp = mp(ps.x + dx[t], ps.y + dy[t]);
if(!(1 <= tp.x && tp.x <= n && 1 <= tp.y && tp.y <= m) || Obs.Find(tp)){ is_1 = 1; break; }
}
if(is_1) break;
}
if(is_1){ puts("1"); continue; } puts("2");
} return 0;
}

BZOJ 不让用 time() 函数,本来随机种子是 time(0) 的。

[UOJ#220][BZOJ4651][Noi2016]网格的更多相关文章

  1. UOJ#220. 【NOI2016】网格 Tarjan

    原文链接www.cnblogs.com/zhouzhendong/p/UOJ220.html 前言 真是一道翔题. 草率题解 -1 的情况很好判,只有两种情况: n * m - c < 2 或者 ...

  2. [BZOJ4651][NOI2016]网格(Tarjan)

    下面直接给出结论,相关证明见官方题解. 1.若跳蚤数不超过1或仅有两只跳蚤且相邻,则答案为-1. 2.若跳蚤形成的连通块个数大于1,则答案为0. 3.若跳蚤之间建图存在割点,则答案为1. 4.否则为2 ...

  3. BZOJ4651 NOI2016网格(割点)

    首先显然可以通过孤立角落里的跳蚤使其不连通,所以只要有解答案就不会大于2.同样显然的一点是当且仅当跳蚤数量<=2且连通时无解.做法其实也很显然了:特判无解,若跳蚤不连通输出0,否则看图中是否无割 ...

  4. [UOJ#223][BZOJ4654][Noi2016]国王饮水记

    [UOJ#223][BZOJ4654][Noi2016]国王饮水记 试题描述 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳 ...

  5. [UOJ#221][BZOJ4652][Noi2016]循环之美

    [UOJ#221][BZOJ4652][Noi2016]循环之美 试题描述 牛牛是一个热爱算法设计的高中生.在他设计的算法中,常常会使用带小数的数进行计算.牛牛认为,如果在 k 进制下,一个数的小数部 ...

  6. [UOJ#219][BZOJ4650][Noi2016]优秀的拆分

    [UOJ#219][BZOJ4650][Noi2016]优秀的拆分 试题描述 如果一个字符串可以被拆分为 AABBAABB 的形式,其中 A 和 B 是任意非空字符串,则我们称该字符串的这种拆分是优秀 ...

  7. BZOJ4651 & 洛谷1173 & UOJ220:[NOI2016]网格——题解(附debug数据)

    https://www.lydsy.com/JudgeOnline/problem.php?id=4651 https://www.luogu.org/problemnew/show/P1173#su ...

  8. BZOJ4651/UOJ220 [Noi2016]网格

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

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

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

随机推荐

  1. NSTimer 实现时钟回调方法

    在开发过程中,发现时钟调用的地方比较多.所以对时钟进行了一个简单的统一封装.具体代码如下: 1.时钟回调函数的声明: #pragma mark 时钟回调处理 //时钟回调 +(NSTimer*) ls ...

  2. SQL根据出生日期精确计算年龄、获取日期中的年份、月份

    第一种: 一张人员信息表里有一人生日(Birthday)列,跟据这个列,算出该人员的年龄 datediff(year,birthday,getdate()) 例:birthday = '2003-3- ...

  3. make与makefile的几个例子和(自己写一下,汗!忘记了!)总结

    共用的几个源代码文件: main.c 2.c 3.c 代码依次为: #include<stdlib.h> #include "a.h" extern void func ...

  4. Javascript 日期格式化

    Javascript 日期格式化 需求: 给出:日期 .格式,根据日期格式进行输出. Date.prototype.Format = function (fmt) { //author: meizz ...

  5. Asp.Net Core 入门(九)—— 环境变量 TagHelper

    我们在之前讲Program.cs文件做了什么的时候,提到启动CreaeDefaultBuilder会获取环境变量来做一些判断之类的操作.那么我们的Taghelper也可以使用“ASPNETCORE_E ...

  6. ratio_to_report分析函数求占比

    drop table test; create table test ( name varchar(20), kemu varchar(20), score number  ); insert int ...

  7. javase(5)_面向对象

    一.概述 1.面向对象是一种思想,让我们由执行者变成指挥者,执行者是面向过程,指挥者是面向对象.例如人开冰箱门,开冰箱门这个动作应该属于门而不是人,冰箱自己最清楚门应该怎么开,人只是调用了冰箱的这个动 ...

  8. javaEE(1)_web开发入门

    一.WEB开发的相关知识 1.WEB,在英语中web即表示网页的意思,它用于表示Internet主机上供外界访问的资源. Internet上供外界访问的Web资源分为: 静态web资源(如html 页 ...

  9. 【meet in middle】poj1840Eqs

    震惊!map的常数居然如此之大 Description Consider equations having the following form: a1x13+ a2x23+ a3x33+ a4x43 ...

  10. Vue的响应式规则

    对象属性的响应规则 <body> <div id="root"> {{msg}} </div> </body> <script ...