给出一张 $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; }