1. 程式人生 > >【uoj#142】【UER #5】萬聖節的南瓜燈 亂搞+並查集

【uoj#142】【UER #5】萬聖節的南瓜燈 亂搞+並查集

light ros 描述 time 並查集 因此 cstring 由於 無環

題目描述

給出一張 $n\times m$ 的網格圖,兩個格子之間有一條雙向邊,當且僅當它們相鄰,即在網格圖中有一條公共邊。

特殊地,對於 $1\le x\le n?$ ,$(x,1)?$ 和 $(x,m)?$ 也視為相鄰。但對於 $1\le y\le m?$ ,$(1,y)?$ 和 $(n,y)?$ 不視為相鄰。

現在這張網格圖有 $k?$ 個格子壞掉了,你需要判斷剩下的部分是否形成一張無向無環連通圖。

$n,m\le 10^9$ ,$k\le 10^5$ 。


題解

亂搞+並查集

對於剩下的圖:點數為 $nm-k$ ,邊數大於等於 $2nm-m-4k$ 。

由於剩下的部分是一棵樹,因此有點數大於邊數,即 $nm-k>2nm-m-4k$ 。

整理得 $(n-1)·m<3k$ 。

由於 $k$ 只有 $10^5$ ,因此 $(n-1)·m$ 只有 $3\times 10^5$ 。又因為 $n\le 3$ ,因此 $nm$ 也只有 $4.5\times 10^5$ 。

經過構造後得出最大的 $nm$ 在 $n=4,m=99999$ 時取到,為 $399996$ 。

因此當滿足 $(n-1)·m<3k$ 時暴力(數組要開到 $449997$ 以上),否則輸出No即可。時間復雜度 $O(449997T)$ 。

或當滿足 $nm\le 400000$ 時暴力(數組要開到 $400000$ 以上),否則輸出No即可。時間復雜度 $O(400000T)$ 。

#include <cstdio>
#include <cstring>
#define pos(i , j) ((i - 1) * m + j)
int v[450010] , f[450010];
int find(int x)
{
	return x == f[x] ? x : f[x] = find(f[x]);
}
int main()
{
	int T;
	scanf("%d" , &T);
	while(T -- )
	{
		memset(v , 0 , sizeof(v));
		int n , m , k , i , j , x , y , c = 0;
		scanf("%d%d%d" , &n , &m , &k);
		if(1ll * n * m >= m + 3 * k)
		{
			while(k -- ) scanf("%*d%*d");
			printf("No");
			if(T) puts("");
			continue;
		}
		for(i = 1 ; i <= k ; i ++ ) scanf("%d%d" , &x , &y) , v[pos(x , y)] = 1;
		for(i = 1 ; i <= n * m ; i ++ ) f[i] = i;
		for(i = 1 ; i <= n ; i ++ )
		{
			for(j = 1 ; j <= m ; j ++ )
			{
				if(!v[pos(i , j)])
				{
					if(i < n && !v[pos(i + 1 , j)]) f[find(pos(i , j))] = find(pos(i + 1 , j)) , c ++ ;
					if(!v[pos(i , j % m + 1)]) f[find(pos(i , j))] = find(pos(i , j % m + 1)) , c ++ ;
				}
			}
		}
		if(c != n * m - k - 1) printf("No");
		else
		{
			for(i = 1 ; i <= n * m ; i ++ )
				if(!v[i])
					x = find(i);
			for(i = 1 ; i <= n * m ; i ++ )
				if(!v[i] && find(i) != x)
					break;
			if(i <= n * m) printf("No");
			else printf("Yes");
		}
		if(T) puts("");
	}
	return 0;
}

【uoj#142】【UER #5】萬聖節的南瓜燈 亂搞+並查集