【BZOJ4651】【NOI2016】网格(Tarjan,哈希)

题面

BZOJ

洛谷

题解

首先把题目稍微变得好说一些,给定一个网格,已经删去了若干个格子

问最少删去多少个格子使得图不连通。

这题的关键是要看出答案一定只有\(-1,0,1,2\)

证明一下一定存在答案不超过二。

在不是无解的情况下,四个角上的答案一定不会超过\(2\)

假设四个角被删去了,那么在边界上一定存在一个未被删去的格子,

一边抵着边界另外一边和一个被删去的格子相邻

(你可以认为这个角被删去了,然后这个角的限制就移动到了这个角的两侧,那么此时这个角旁边的两个点一定一端抵住了这个被删去的角的那一条边,另外一端抵着边界)

如果整个边界都被删去,那么这一行(列)就没有任何意义,可以直接考虑上面那一行(列)

所以答案一定不会超过\(2\)。

那么只需要分情况讨论即可。

我们发现图非常非常大,一点也不好搞。

所以我们按照\(x,y\)两轴分别离散,只需要离散所有被删除的点

而离散的时候额外加入左右(上下)两侧的行(列)就好了。当然,要加入两行(列)

因为如果只加入一列,会有问题,具体的反例就不给出来了。

也就是一个点加入以他为中心的,距离它曼哈顿距离为为\(4\)以内的所有点。

首先考虑答案为\(-1\)的情况

无解是什么呢?首先如果只有一个格子,显然无解。

还有一种情况,只有两个格子,并且这两个格子相邻,那么也必定无解。

如果超过了三个格子,因为这三个格子无法做到两两直接联通,所以就不存在无解的情况了。

再考虑一下\(0\)的情况,也就是啥都不用做,图就已经不连通了。

那么,考虑把所有已经被删除的格子的联通块给搞出来。

显然这个联通块周围的所有点都要在同一个集合中,直接\(bfs\)判断即可。

答案为\(1\)的时候就是在图中存在割点,并且这个割点一定和一个被删去的位置相邻。

剩下的所有情况就是\(2\)了。

我到现在还不知道为啥在UOJ上过不了Hack数据。。。。。

所有的点全部使用\(Hash\)来存储。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define ll long long
#define RG register
#define MAX 1000100
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
int d[8][2]={1,0,0,1,-1,0,0,-1,1,1,-1,-1,-1,1,1,-1};
int n,m,C;
ll id(int i,int j){return 1ll*(i-1)*m+j;}
struct Node{int x,y;}p[MAX];
int S[MAX],len;
int vis[MAX],fr[MAX];
const int hashmod=1000007;
struct HashTable
{
struct Node{int nt,i,j;}e[MAX<<3];
int h[hashmod],cnt;
void init(){memset(h,0,sizeof(h));cnt=0;}
int Query(int i,int j)
{
int x=id(i,j)%hashmod;
for(int u=h[x];u;u=e[u].nt)
if(e[u].i==i&&e[u].j==j)return u;
return 0;
}
void insert(int i,int j)
{
int x=id(i,j)%hashmod;
e[++cnt]=(Node){h[x],i,j};h[x]=cnt;
}
}H;
bool checkNoAns()
{
if(1ll*n*m-C>2)return false;
if(1ll*n*m-C<2)return true;
H.init();
for(int i=1;i<=C;++i)H.insert(p[i].x,p[i].y);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
if(!H.Query(i,j))
{
if(i!=1)if(!H.Query(i-1,j))return true;
if(i!=n)if(!H.Query(i+1,j))return true;
if(j!=1)if(!H.Query(i,j-1))return true;
if(j!=m)if(!H.Query(i,j+1))return true;
}
return false;
}
struct Line{int v,next;}e[MAX<<3];
int h[MAX],cnt=1;
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
bool cut[MAX];
int dfn[MAX],low[MAX],tim,gr;
vector<int> V[MAX];
void Connect(int x,int gr)
{
vis[x]=gr;V[gr].push_back(x);
for(int k=0;k<4;++k)
{
int xx=p[x].x+d[k][0],yy=p[x].y+d[k][1];
int v=H.Query(xx,yy);
if(v&&v<=C&&!vis[v])Connect(v,gr);
}
}
void pre()
{
memset(fr,0,sizeof(fr));memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
memset(cut,0,sizeof(cut));memset(h,0,sizeof(h));
for(int i=1;i<=gr;++i)V[i].clear();gr=0;
tim=0;cnt=1;H.init();
for(int i=1;i<=C;++i)H.insert(p[i].x,p[i].y);
for(int i=1;i<=C;++i)if(!vis[i])Connect(i,++gr);
for(int i=1;i<=C;++i)
for(int k=0;k<8;++k)
{
int x=p[i].x+d[k][0],y=p[i].y+d[k][1];
if(x<1||y<1||x>n||y>m)continue;
int u=H.Query(x,y);if(u)continue;
H.insert(x,y);fr[H.cnt]=true;
}
for(int i=H.cnt;i>C;--i)
{
int x=H.e[i].i,y=H.e[i].j;
for(int k=0;k<8;++k)
{
int xx=x+d[k][0],yy=y+d[k][1];
if(xx<1||yy<1||xx>n||yy>m)continue;
if(H.Query(xx,yy))continue;
H.insert(xx,yy);
}
}
}
void bfs(int u,int tt)
{
queue<int> Q;Q.push(u);vis[u]=tt;
while(!Q.empty())
{
int u=Q.front();Q.pop();
for(int k=0;k<4;++k)
{
int x=H.e[u].i+d[k][0],y=H.e[u].j+d[k][1];
int i=H.Query(x,y);
if(i<=C||vis[i])continue;
vis[i]=tt;Q.push(i);
}
}
}
bool check0()
{
int tt=0;
for(int i=C+1;i<=H.cnt;++i)if(!vis[i])bfs(i,++tt);
for(int s=1;s<=gr;++s)
{
int dsu=-1;
for(int l=V[s].size()-1;~l;--l)
{
int i=V[s][l];
for(int d1=-2;d1<=2;++d1)
for(int d2=-2;d2<=2;++d2)
{
int x=p[i].x+d1,y=p[i].y+d2;
if(x<1||y<1||x>n||y>m)continue;
int u=H.Query(x,y);if(u<=C)continue;
if(dsu!=-1&&dsu!=vis[u])return true;
else dsu=vis[u];
}
}
}
return false;
}
void Tarjan(int u,int ff)
{
dfn[u]=low[u]=++tim;int son=0;
for(int i=h[u];i;i=e[i].next)
{
int v=e[i].v;if(v==ff)continue;
if(!dfn[v])
{
++son;Tarjan(v,u);
if(low[v]>=dfn[u])cut[u]=true;
low[u]=min(low[u],low[v]);
}
else low[u]=min(low[u],dfn[v]);
}
if(!ff&&son<2)cut[u]=false;
}
bool check1()
{
if(n==1||m==1)return true;
for(int i=H.cnt;i>C;--i)
{
int x=H.e[i].i,y=H.e[i].j;
for(int k=0;k<4;++k)
{
int xx=x+d[k][0],yy=y+d[k][1];
if(xx<1||yy<1||xx>n||yy>m)continue;
int u=H.Query(xx,yy);
if(u<=C)continue;Add(i,u);
}
}
for(int i=1;i<=H.cnt;++i)if(!dfn[i])Tarjan(i,0);
for(int i=C+1;i<=H.cnt;++i)if(cut[i]&&fr[i])return true;
return false;
}
int main()
{
int TT=read();
while(TT--)
{
n=read();m=read();C=read();
for(int i=1;i<=C;++i)p[i].x=read(),p[i].y=read();
if(checkNoAns()){puts("-1");continue;}
if(!C){if(n==1||m==1)puts("1");else puts("2");continue;}
len=0;
for(int i=1;i<=C;++i)
{
S[++len]=p[i].x;S[++len]=p[i].x+1;S[++len]=p[i].x+2;
if(p[i].x>1)S[++len]=p[i].x-1;
if(p[i].x>2)S[++len]=p[i].x-2;
}
sort(&S[1],&S[len+1]);len=unique(&S[1],&S[len+1])-S-1;
while(S[len]>n)--len;n=len;
for(int i=1;i<=C;++i)p[i].x=lower_bound(&S[1],&S[len+1],p[i].x)-S;
len=0;
for(int i=1;i<=C;++i)
{
S[++len]=p[i].y;S[++len]=p[i].y+1;S[++len]=p[i].y+2;
if(p[i].y>1)S[++len]=p[i].y-1;
if(p[i].y>2)S[++len]=p[i].y-2;
}
sort(&S[1],&S[len+1]);len=unique(&S[1],&S[len+1])-S-1;
while(S[len]>m)--len;m=len;
for(int i=1;i<=C;++i)p[i].y=lower_bound(&S[1],&S[len+1],p[i].y)-S;
pre();if(check0()){puts("0");continue;}if(check1()){puts("1");continue;}
puts("2");
}
}

【BZOJ4651】【NOI2016】网格(Tarjan,哈希)的更多相关文章

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

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

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

    [UOJ#220][BZOJ4651][Noi2016]网格 试题描述 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有 ...

  3. BZOJ4651 NOI2016网格(割点)

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

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

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

  5. BZOJ4651/UOJ220 [Noi2016]网格

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

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

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

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

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

  8. [Noi2016]网格

    来自FallDream的博客,未经允许,请勿转载,谢谢.   跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐, ...

  9. 洛谷P1173 [NOI2016]网格

    这个码量绝对是业界大毒瘤...... 300行,6.5k,烦的要死...... 题意:给你一个网格图,里面有0或1.你需要把一些0换成1使得存在某两个0不四联通.输出最小的换的数量.无解-1. n,m ...

  10. [BZOJ]4650 优秀的拆分(Noi2016)(哈希+二分)

    传送门   题解 听说大佬们这题都是用SA秒掉的 然而SA的时间复杂度的确很优秀,缺点就是看不太懂…… 然后发现一位大佬用哈希华丽的过了此题,而且讲的特别清楚->这里 我们只要考虑以每一个点结尾 ...

随机推荐

  1. macOS 10.14 Mojave Apache设置:多个PHP版本

    [18/6/2018更新]由于Homebrew/php点击在2018年3月底被弃用,并将所有PHP公式移动到Homebrew/core,我们已经重新设计了我们的指南,使用这个新的水龙头. 如果您过去一 ...

  2. Python3中IO文件操作的常见用法

    首先创建一个文件操作对象: f = open(file, mode, encoding) file指定文件的路径,可以是绝对路径,也可以是相对路径 文件的常见mode: mode = “r”   # ...

  3. Unity初级案例——贪吃蛇

    using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; ...

  4. PHP性能优化 -理论篇

    什么情况下,遇到了PHP性能问题?    1 PHP语法使用的不恰当    2 使用PHP语言做不了它不擅长做的事    3 用php语言连接的服务不给力    4 PHP自身的短板    5 我也不 ...

  5. xpath获取同级元素

    XPath轴(XPath Axes)可定义某个相对于当前节点的节点集: 1.child 选取当前节点的所有子元素 2.parent 选取当前节点的父节点 3.descendant 选取当前节点的所有后 ...

  6. 如何理解IPD+CMMI+Scrum一体化研发管理解决方案之CMMI篇

    如何快速响应市场的变化,如何推出更有竞争力的产品,如何在竞争中脱颖而出,是国内研发企业普遍面临的核心问题,为了解决这些问题,越来越多的企业开始重视创新与研发管理,加强研发过程的规范化,集成产品开发(I ...

  7. redis 学习记录

    http://www.yiibai.com/redis/redis_quick_guide.html Redis 是一款依据BSD开源协议发行的高性能Key-Value存储系统(cache and s ...

  8. Segments CodeForces 909B (找规律)

    Description You are given an integer N. Consider all possible segments (线段,划分)on the coordinate axis ...

  9. 王者荣耀交流协会final发布版本控制报告

    二次开发软件说明文档 Dec 6 纠正饼状图点击选择PSP文件无效. 添加饼状图丢失的代码. submit the files that last night I forgot. add shurum ...

  10. bat获取当前日期的前一天

    批处理做这样的事情很麻烦,你可以用cscript来实现,比如把下面的内容保存为a.js文件:var d=new Date();d.setTime(d.getTime()-24*3600*1000);v ...