[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。否则,输出被替换的跳蚤的个数的最小值

输入示例

4
4 4 2
1 1
4 4
2 3 1
1 2
2 2 2
1 1
2 2
1 1 0

输出示例

2
1
0
-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) 的。

时间: 2024-08-06 11:54:17

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

BZOJ4651 [Noi2016]网格/UOJ220

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐,其余的格子中,每个格子有一只跳蚤. 我们称占据的格子有公共边的两只跳蚤是相邻的. 我们称两只跳

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

试题描述 跳蚤国有 n 个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 1 号城市中.跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王又体恤地将分配给他的水也给跳蚤国居民饮用,这导致跳蚤国王也经常喝不上水.于是,跳蚤国在每个城市都修建了一个圆柱形水箱,这些水箱完全相同且足够高.一个雨天后,第 i 个城市收集到了高度为 hi 的水.由于地理和天气因素的影响,任何两个不同城市收集到的水高度互不相同.跳蚤国王也请来蚂蚁工匠帮忙,建立了一个庞大的地下连通系统.跳蚤国王每次使用地下连通系

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

试题描述 牛牛是一个热爱算法设计的高中生.在他设计的算法中,常常会使用带小数的数进行计算.牛牛认为,如果在 k 进制下,一个数的小数部分是纯循环的,那么它就是美的.现在,牛牛想知道:对于已知的十进制数 n 和 m,在 k 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 x/y 表示,其中 1≤x≤n,1≤y≤m,且 x,y是整数.一个数是纯循环的,当且仅当其可以写成以下形式:a.c1˙c2c3-cp-1cp˙其中,a 是一个整数,p≥1:对于 1≤i≤p,ci是 kk 进制下的一位数字.

UOJ #221 【NOI2016】 循环之美

题目链接:循环之美 这道题感觉非常优美--能有一个这么优美的题面和较高的思维难度真的不容易-- 为了表示方便,让我先讲一下两个符号.$[a]$表示如果$a$为真,那么返回$1$,否则返回$0$: $a \perp b$表示$a$与$b$互质. 首先,我们需要考虑一个分数要成为纯循环小数需要满足什么条件. 我们先来回想一下,我们是怎样使用除法来判断一个分数$\frac{x}{y}$是否是纯循环小数的.显然我们是一路除下去,什么时候出现了相同的余数,那么这个数就是一个循环小数.如果第一个重复的余数是

[UOJ #222][NOI2016]区间(线段树)

Description 在数轴上有 n个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m个区间共同包含至少一个位置.换句话说,就是使得存在一个 x,使得对于每一个被选中的区间 [li,ri],都有 li≤x≤ri. 对于一个合法的选取方案,它的花费为被选中的最长区间长度减去被选中的最短区间长度.区间 [li,ri] 的长度定义为 ri−li,即等于它的右端点的值减去左端点的值 求所有合法方案中最小的花费.如果不存在合法的方案,输出 −1. S

[bzoj4651]网格

考虑将最上中最左的跳蚤孤立,容易发现他的上面和左面没有跳蚤,因此只需要将他的右边和下边替换掉就可以了答案为-1有两种情况:1.c>=n*m-1;2.c=n*m-2且这两只跳蚤相邻对于其他情况,将所有跳蚤建图后判断:1.是否有多个连通块:2.是否有割点即可,由于跳蚤数量很多,容易发现只需要选择周围5*5的范围内有蛐蛐的跳蚤建图即可只选择3*3会有反例(以下q表示蛐蛐,t表示跳蚤):ttttttttqtttttttqtttttttt(注意如果c=0那么答案为((n>1)&&(m&g

【UOJ Round #8】

A 一道不错的题,虽然大家都觉得是水题,然而蒟蒻我想出来的好慢……Orz alpq 发现其实就是一个网格图,每一个大块都是同一颜色……横纵坐标互不干扰…… 1 //UOJ Round #8 A 2 #include<vector> 3 #include<cstdio> 4 #include<cstring> 5 #include<cstdlib> 6 #include<iostream> 7 #include<algorithm> 8

NOI2016 高中OI生涯的最后一站

你乘坐的航班XXX已经抵达终点站——四川绵阳. “呼——”机舱外的天空灰沉沉的,不禁有些压抑与紧张. 一出机场,就看见南山中学的牌子,黄色衣服的志愿者们,还有热情的老师们. 感觉刚才的情绪又一扫而空了,转而迎来的是一种兴奋与激动. 学长和教练都曾说过:就当做一次展现自己实力的机会.从来不要给自己太大压力. 这样的话大多也埋在心里了吧,潜移默化的影响着自己的心情. 那就开心的去面对这几场考试好了. 首先领好东西,还去签名版上签了个名字,发现湖南参赛的选手果然好多呀...不过不知道今年能不能翻身成强

轴的网格划分

轴的规则网格生成: 图8-1所示为轴的示意图,要求生成图8-2所示的规则六面体网格单元. 图8-2 生成的网格图 用自底向上建模方式生成的具体操作是: 1)选择最大圆面所在的位置,按不同的半径生成关键点,然后再按半径连成线,通过生成的半径线绕中心旋转生成一个面,对面进行重经操作后,就形成了不同半径的圆面或圆环面. 2)对圆面和圆环面生成二维单元(plane42),其中为了保证生成的单元都是六面体,在圆面的中心又生成了一个正方形面,然后对其划分曲格,生成的结果如图8-3b所示. 3)改变单元的属性