BZOJ4651/UOJ220 [Noi2016]网格
本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。
本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!
Description
Input
Output
Sample Input
4 4 2
1 1
4 4
2 3 1
1 2
2 2 2
1 1
2 2
1 1 0
Sample Output
1
0
-1
explanation
第一组数据就是问题描述中的例子。
对于第二组数据,可以将第 2 行第 2 列的一只跳蚤替换为蛐蛐,从而使得存在两只跳蚤不连通
并且不存在更优的方案。
对于第三组数据,最初已经存在两只跳蚤不连通,故不需要再进行替换。
对于第四组数据,由于最多只有一只跳蚤,所以无论如何替换都不能存在两只跳蚤不连通
正解:hash+tarjan求割点
解题报告:
这道题我当时在考场上很快反应过来答案只有可能是0、1、2、-1…
部分分我在讲题的时候已经详细说了,所以直接上正解好了嘿嘿嘿...
考虑如果有大量空行则显然是无用的,具体思考后发现,对于一个蛐蛐,只有上下两行,左右两列是有用的,深入思考发现,我们只需要考虑每个蛐蛐的周围24个格子即可,直接相邻的叫做一级空地,和一级空地相邻的叫二级空地。
官方题解给出了详细证明,不赘述其正确性,下面只讲做法:
无解的情况可以很容易处理:假设跳蚤数量<=1或者只有2个跳蚤且相邻则无解;
然后我们对于每个蛐蛐控制的范围,扣出所有的空地,按四联通建图,
0个的情况是当把所有的连通块弄出来之后,假设一个蛐蛐控制的范围内的点,有不连通的空地,则说明已经隔断了;
1的情况考虑本质是求出有无割点,对建出来的图中每个连通块跑tarjan,假如存在割点则答案是1;
上述情况均不满足,答案就是2。
但是很遗憾的是这么做只有60分,我WA了很久之后发现,割点只有在一级空地时才有效,在二级空地的情况是非法的!
加入这条判断之后就AC了...
这道题的细节比较多,似乎我为了方便写的是函数化形式,有点丑,而且代码长...
讲道理这道题只要概括出模型,算法还是很simple的,就是要想到不断地优化来降低图的大小。
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MOD = 400007;
const int MAXN = 200011;
const int Bas = 1000000007;
const int MAXM = 5000011;
int n,m,c,yi[12][2]={{1,0},{0,1},{-1,0},{0,-1}};
int father[MAXM],ans,top;
LL dui[MAXM];
struct Cricket{ int x,y; }b[MAXN];
struct Hash{
int ecnt,first[MOD+12],next[MAXM];
LL to[MAXM];
inline void init(){ ecnt=0; memset(first,0,sizeof(first)); }
inline void insert(LL x,LL y){
LL num=(x*Bas+y); int cc=(int)(num%MOD);
next[++ecnt]=first[cc]; first[cc]=ecnt; to[ecnt]=num;
} inline int query(LL x,LL y){
LL num=(x*Bas+y); int cc=(int)(num%MOD);
for(int i=first[cc];i;i=next[i]) if(to[i]==num) return i;
return -1;
}
}mp1,mp2; inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} namespace Graph{
const int maxn = 2400011;
const int maxm = 10000011;
int ecnt,first[maxn],to[maxm],next[maxm],dfn[maxn],low[maxn],cnt,kcnt,stack[maxn],stack_top;
bool iscut[maxn],yiji[maxn];
inline void init(){ ecnt=0; memset(first,0,sizeof(first)); memset(yiji,0,sizeof(yiji)); }
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
/*inline int find(int x){
stack_top=0;
while(father[x]!=x) {
stack[++stack_top]=x;
x=father[x];
}
while(stack_top>0) {
father[stack[stack_top]]=x;
stack_top--;
}
return x;
}*/
inline void link(int x,int y){
next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y;
next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x;
}
inline void tarjan(int x,int fa){
dfn[x]=low[x]=++cnt; int size=0;
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
if(dfn[v]==-1) {
size++; tarjan(v,x);
low[x]=min(low[x],low[v]);
if(low[v]>=dfn[x]) iscut[x]=1;
}
else if(low[v]<low[x]) low[x]=low[v];
}
if(fa==-1 && size==1) iscut[x]=0;
}
inline void work(){
if((LL)n*m-c==2 && mp2.ecnt==2) { if(to[first[1]]==2) { puts("-1"); return ; } }//特判只有相邻的两个的情况
for(int i=1;i<=mp2.ecnt;i++) dfn[i]=low[i]=-1,iscut[i]=0; cnt=0; kcnt=0;
for(int i=1;i<=mp2.ecnt;i++) if(dfn[i]==-1) { kcnt++,tarjan(i,-1); }
if(n==1 || m==1) { puts("1"); return ; }//特判n、m等于1的情况
for(int i=1;i<=mp2.ecnt;i++) if(yiji[i] && iscut[i]) { puts("1"); return ; }
puts("2");
}
} inline void build(int inx,int iny){
int nowx,nowy,nx,ny,nn;
for(int i=-2;i<=2;i++)
for(int j=-2;j<=2;j++) {
if(i==0 && j==0) continue;
nowx=inx+i; nowy=iny+j; if(nowx<=0 || nowy<=0 || nowx>n || nowy>m) continue;
if(mp1.query(nowx,nowy)!=-1) continue; nn=mp2.query(nowx,nowy);
if(nn!=-1) { if(abs(i)<=1 && abs(j)<=1) Graph::yiji[nn]=1; continue; }
mp2.insert(nowx,nowy); nn=mp2.query(nowx,nowy);
if(abs(i)<=1 && abs(j)<=1) Graph::yiji[nn]=1;
for(int l=0;l<4;l++) {
nx=nowx+yi[l][0]; ny=nowy+yi[l][1]; if(nx<=0 || ny<=0 || nx>n || ny>m) continue;
if(mp1.query(nx,ny)!=-1) continue; if(mp2.query(nx,ny)==-1) continue;
Graph::link(nn,mp2.query(nx,ny));
}
}
} inline void check_init(){
using namespace Graph;
int r1,r2; for(int i=1;i<=mp2.ecnt;i++) father[i]=i;
for(int i=1;i<=mp2.ecnt;i++)
for(int j=first[i];j;j=next[j]) {
if(to[j]<=i) continue;
r1=find(i); r2=find(to[j]);
if(r1!=r2) father[r1]=r2;
}
} inline void check(int inx,int iny){
using namespace Graph;
int nowx,nowy; top=0;
for(int i=-2;i<=2;i++)
for(int j=-2;j<=2;j++){
if(i==0 && j==0) continue;
nowx=inx+i; nowy=iny+j; if(nowx<=0 || nowy<=0 || nowx>n || nowy>m) continue;
if(mp1.query(nowx,nowy)!=-1) continue;
dui[++top]=mp2.query(nowx,nowy);
}
for(int i=2;i<=top;i++) if(find(dui[i])!=find(dui[1])) { ans=0; return ; }
} inline void work(){
int T=getint();
while(T--) {
n=getint(); m=getint(); c=getint();
for(int i=1;i<=c;i++) b[i].x=getint(),b[i].y=getint();
if((LL)n*m-c<=1) { puts("-1"); continue; }//特判无解
if(c==0 && n*m==2) { puts("-1"); continue; }//特判只有相邻的两个的情况
mp1.init(); mp2.init(); Graph::init(); ans=-1;
for(int i=1;i<=c;i++) mp1.insert(b[i].x,b[i].y);
for(int i=1;i<=c;i++) build(b[i].x,b[i].y);
//检查0的情况
check_init();
for(int i=1;i<=c;i++) { check(b[i].x,b[i].y); if(ans==0) break; }
if(ans==0) { puts("0"); continue; }
Graph::work();
}
} int main()
{
work();
return 0;
}
BZOJ4651/UOJ220 [Noi2016]网格的更多相关文章
- UOJ220 [NOI2016] 网格 【割顶】【并查集】
题目分析: 答案显然只有{-1,0,1,2}四种. 对于答案等于-1的情况,只有两种情况,一种是只剩一只跳蚤,另一种是只剩两只跳蚤且他们四连通,这个很好判. 对于答案等于0的情况,那说明联通块大于1, ...
- [UOJ#220][BZOJ4651][Noi2016]网格
[UOJ#220][BZOJ4651][Noi2016]网格 试题描述 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有 ...
- BZOJ4651 & 洛谷1173 & UOJ220:[NOI2016]网格——题解(附debug数据)
https://www.lydsy.com/JudgeOnline/problem.php?id=4651 https://www.luogu.org/problemnew/show/P1173#su ...
- 并不对劲的bzoj4651:loj2084:uoj220:p1173:[NOI2016]网格
题目大意 有一个\(n*m\)(\(n,m\leq10^9\))的网格,每个格子是空地或障碍(\(障碍数\leq10^5\)) 定义两块空地连通,当且仅当它们是"相邻的两块空地"或 ...
- [BZOJ4651][NOI2016]网格(Tarjan)
下面直接给出结论,相关证明见官方题解. 1.若跳蚤数不超过1或仅有两只跳蚤且相邻,则答案为-1. 2.若跳蚤形成的连通块个数大于1,则答案为0. 3.若跳蚤之间建图存在割点,则答案为1. 4.否则为2 ...
- BZOJ4651 NOI2016网格(割点)
首先显然可以通过孤立角落里的跳蚤使其不连通,所以只要有解答案就不会大于2.同样显然的一点是当且仅当跳蚤数量<=2且连通时无解.做法其实也很显然了:特判无解,若跳蚤不连通输出0,否则看图中是否无割 ...
- [Noi2016]网格
来自FallDream的博客,未经允许,请勿转载,谢谢. 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐, ...
- 洛谷P1173 [NOI2016]网格
这个码量绝对是业界大毒瘤...... 300行,6.5k,烦的要死...... 题意:给你一个网格图,里面有0或1.你需要把一些0换成1使得存在某两个0不四联通.输出最小的换的数量.无解-1. n,m ...
- NOI2016
luoguP1712 [NOI2016]区间 这是一道送分题. 对于我这种每天抄题解不动脑子思维僵化得厉害的智障选手就是送命题. 一直在想端点排序各种Treap搞... 正解: 已知一些区间,如何判断 ...
随机推荐
- java后台去除JSON数组的重复值
假设原始Json数组是这样的 原始JSONArry:[{"Value":"15153129877","Key":"09770985 ...
- Yaf学习(二)----Yaf初体验
1.hello world 1.1 用yaf输出hello world 1.首先配置host,nginx 2.host不用多说,指向虚拟机IP即可 1.2 重点说一下nginx (只说server块) ...
- PHP计算两个时间戳之间间隔时分秒
/功能:计算两个时间戳之间相差的日时分秒//$begin_time 开始时间戳//$end_time 结束时间戳function timediff($begin_time,$end_time){ if ...
- Python学习手册之控制结构(一)
在上一篇文章中,我们对 Python 进行了简单介绍和介绍了 Python 的基本语法,现在我们继续介绍 Python 控制结构. 查看上一篇文章请点击:https://www.cnblogs.com ...
- dz论坛Discuz_X3.4最新网站漏洞
近期我们sinesafe安全部门审计discuz最新版的时候发现配置文件写入导致代码执行的问题.cms安装的时候一般会分为几个步骤去进行,其中有对配置文件config进行写入的步骤,当写入的时候未严格 ...
- MFC接收ShellExecute多个参数
在应用程序开发过程中,我们经常需要带参数启动另一个执行程序,如何传递多个参数,如何解析多个参数呢? 传参数 传递参数可使用ShellExecute函数,示例如下: ShellExecute(NUL ...
- [Cracking the Coding Interview] 4.5 Validate BST
Implement a function to check if a binary tree is a binary search tree. 这道题很经典,让我们判断一棵树是不是二叉查找树.但是首先 ...
- docker制作jdk+tomcat镜像
docker部署TOMCAT项目 一.内核升级 [root@test01 ~]# uname -r #内核查看确认 2.6.32-696.16.1.el6.x86_64 [root@test01 ...
- 初步学习pg_control文件之十四
接前文 初步学习pg_control文件之十三 看如下几个: /* * Parameter settings that determine if the WAL can be used for arc ...
- pprof 查看goroutine
package main import ( "net/http" "runtime/pprof" ) var quit chan struct{} = make ...