首页 > 代码库 > BZOJ4651 [Noi2016]网格/UOJ220
BZOJ4651 [Noi2016]网格/UOJ220
本文版权归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 [Noi2016]网格/UOJ220