【BZOJ4651】【NOI2016】网格(Tarjan,哈希)
【BZOJ4651】【NOI2016】网格(Tarjan,哈希)
题面
题解
首先把题目稍微变得好说一些,给定一个网格,已经删去了若干个格子
问最少删去多少个格子使得图不连通。
这题的关键是要看出答案一定只有\(-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,哈希)的更多相关文章
- [BZOJ4651][NOI2016]网格(Tarjan)
下面直接给出结论,相关证明见官方题解. 1.若跳蚤数不超过1或仅有两只跳蚤且相邻,则答案为-1. 2.若跳蚤形成的连通块个数大于1,则答案为0. 3.若跳蚤之间建图存在割点,则答案为1. 4.否则为2 ...
- [UOJ#220][BZOJ4651][Noi2016]网格
[UOJ#220][BZOJ4651][Noi2016]网格 试题描述 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有 ...
- BZOJ4651 NOI2016网格(割点)
首先显然可以通过孤立角落里的跳蚤使其不连通,所以只要有解答案就不会大于2.同样显然的一点是当且仅当跳蚤数量<=2且连通时无解.做法其实也很显然了:特判无解,若跳蚤不连通输出0,否则看图中是否无割 ...
- BZOJ4651 & 洛谷1173 & UOJ220:[NOI2016]网格——题解(附debug数据)
https://www.lydsy.com/JudgeOnline/problem.php?id=4651 https://www.luogu.org/problemnew/show/P1173#su ...
- BZOJ4651/UOJ220 [Noi2016]网格
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...
- 并不对劲的bzoj4651:loj2084:uoj220:p1173:[NOI2016]网格
题目大意 有一个\(n*m\)(\(n,m\leq10^9\))的网格,每个格子是空地或障碍(\(障碍数\leq10^5\)) 定义两块空地连通,当且仅当它们是"相邻的两块空地"或 ...
- UOJ#220. 【NOI2016】网格 Tarjan
原文链接www.cnblogs.com/zhouzhendong/p/UOJ220.html 前言 真是一道翔题. 草率题解 -1 的情况很好判,只有两种情况: n * m - c < 2 或者 ...
- [Noi2016]网格
来自FallDream的博客,未经允许,请勿转载,谢谢. 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐, ...
- 洛谷P1173 [NOI2016]网格
这个码量绝对是业界大毒瘤...... 300行,6.5k,烦的要死...... 题意:给你一个网格图,里面有0或1.你需要把一些0换成1使得存在某两个0不四联通.输出最小的换的数量.无解-1. n,m ...
- [BZOJ]4650 优秀的拆分(Noi2016)(哈希+二分)
传送门 题解 听说大佬们这题都是用SA秒掉的 然而SA的时间复杂度的确很优秀,缺点就是看不太懂…… 然后发现一位大佬用哈希华丽的过了此题,而且讲的特别清楚->这里 我们只要考虑以每一个点结尾 ...
随机推荐
- Swift入门基础知识
var //代表变量,变量的值可以改变 let//代表常量类型不可改变 //声明常量heh类型Swift会自动根据你的值来自动判断该变量的类型也可以指定类型(个人感觉还是指定类型的比较好,可能会减少系 ...
- Base64编码后通过Url传值
Base64编码简介 Base编码使用"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",再加上补 ...
- python全栈开发-前方高能-内置函数2
python_day_15 一.今日主要内容 1. lambda 匿名函数 语法: lambda 参数:返回值 不能完成复杂的操作 2. sorted() 函数 排序. 1. 可迭代对象 2. key ...
- 数据库表,id自动递增重置,从1开始
问题: 删除数据表的数据后,发现下次插入数据,主键id并没有重置,还是在原来基础上继续增加 解决: ; TRUNCATE table table_name; ; 参考: https://stackov ...
- katalon系列十六:代码运行时实时创建元素对象或列表
Katalon的常规方法是先抓取元素并保存到仓库,在脚本中需要用到的时候调取,但假如元素属性和个数是可变的,就不能事先保存到仓库了,需要在脚本运行时实时创建. 代码运行时实时创建一个元素对象的例子im ...
- Vue.js项目中,当图片无法显示时则显示默认图片
使用require将图片进入,写法如下: data: () => ({logo: 'this.src="' + require('../assets/img.png') + '&quo ...
- ConcurrentHashMap(JDK1.8)为什么要放弃Segment
今天看到一篇博客:jdk1.8的HashMap和ConcurrentHashMap,我想起了前段时间面试的一个问题:ConcurrentHashMap(JDK1.8)为什么要使用synchronize ...
- 当Kubernets遇上阿里云 -之七层负载均衡(一).
我们知道Kubernetes的service只能实现基于4层的负载均衡,无法提供7层之上的许多特性,诸如基于URL的负载均衡,SSL支持,三方授权等等:Ingress可以实现七层负载均衡的许多功能,唯 ...
- mongodb redis memcache 对比
从以下几个维度,对 Redis.memcache.MongoDB 做了对比. 1.性能 都比较高,性能对我们来说应该都不是瓶颈. 总体来讲,TPS 方面 redis 和 memcache 差不多,要大 ...
- 未能加载文件或程序集 system.Web.Http.WebHost解决办法。
在csdn中找到一个方法: Update-Package Microsoft.AspNet.WebApi -reinstall 然后就好了. 另外一个方法是缺少哪个dll,就复制一个dll放到bin文 ...