[NOI2016] 网格

XXXVIII.[NOI2016] 网格

首先,答案一定 \(\leq2\),因为四个角的跳蚤被围住只需要两个蛐蛐,而如果蛐蛐占住了一个角又会产生新的角。

\(-1\) 的情形比较容易,要么空隙少于 \(2\) 个,要么仅剩的两个空隙在一起。两种情况下 \(n\times m\) 都与 \(c\) 同级别,因此可以暴力。

\(0\) 的情形等价于存在不连通的部分。可以发现,蛐蛐形成的八连通块,找到块中所有节点周边八个跳蚤并跑跳蚤的四联通性。若发现八连通块周边的跳蚤属于两个及以上不同的连通块,则即为 \(0\)

\(1\) 的情形等价于存在割点。割点有两种可能,一是 \(n,m\) 中至少有一个为 \(1\),二是蛐蛐把跳蚤分出了割点。假如依旧只找周边 \(8\) 个跳蚤跑割点的话,那周边一圈的节点可能会被当成割点,比如这个样例,其中 # 代表蛐蛐。

...
...
..#
...
...

但是我们发现,只需要选取周边两圈的节点即可规避上述问题。但是,需要注意的是,割点只能统计周边一圈的节点,否则会被

#....
.....
.....
.....
....#

这组样例干掉——正中央的那个节点是割点,但不是内圈割点,因此不能计入。

然后剩余的情景即为 \(2\)

需要注意的是,本题数据范围很大,存图要么用 unordered_map,要么手写哈希表。这里试了试哈希表,还挺好写的。

代码:

#include<bits/stdc++.h>
using namespace std;
const int M=3001000;
int T,n,m,c,dx[4]={1,0,-1,0},dy[4]={0,1,0,-1},X[100100],Y[100100];
const int P=4999999;
const int HT=5000000;
const int BS=1e9+7;
struct HashTable{
	int head[HT],val[HT],nxt[HT],rx[HT],ry[HT],cnt;
	int HASH(int x,int y){return (1ll*x*m+y)%P;}
	void clear(){memset(head,-1,sizeof(head));}
	void ins(int x,int y,int z){
		int p=HASH(x,y);
		nxt[cnt]=head[p],val[cnt]=z,rx[cnt]=x,ry[cnt]=y,head[p]=cnt++;
	}
	int fnd(int x,int y){for(int i=head[HASH(x,y)];i!=-1;i=nxt[i])if(rx[i]==x&&ry[i]==y)return val[i];return -1;}
}m1,m2;
bool checkimp(){//the function for impossibility check.
	if(1ll*n*m-c<2)return true;
	if(1ll*n*m-c>2)return false;
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){
		if(m1.fnd(i,j)!=-1)continue;
		for(int k=0;k<4;k++){
			int I=i+dx[k],J=j+dy[k];
			if(!I||!J||I>n||J>m||m1.fnd(I,J)!=-1)continue; 
			return true;
		}
	}
	return false;
}
int cnt,bcm,cid[100100],dsu[100100];
pair<int,int>p[M];
vector<int>v[M];
int blk[M];
void floodfill(int x){
	blk[x]=bcm;
	for(auto y:v[x])if(!blk[y])floodfill(y);
}
int find(int x){return dsu[x]==x?x:dsu[x]=find(dsu[x]);}
void merge(int x,int y){x=find(x),y=find(y);if(x!=y)dsu[y]=x;}
bool checkdisc(){//check if the graph is disconnected.
	for(int i=1;i<=cnt;i++)if(!blk[i])bcm++,floodfill(i);
	for(int i=1;i<=c;i++)dsu[i]=i,cid[i]=0;
	for(int i=1;i<=c;i++)for(int j=-1;j<=1;j++)for(int k=-1;k<=1;k++){
		if(!j&&!k||X[i]+j<1||Y[i]+k<1||X[i]+j>n||Y[i]+k>m)continue;
		int tmp=m1.fnd(X[i]+j,Y[i]+k);
		if(tmp!=-1)merge(i,tmp);
	}
	for(int i=1;i<=c;i++)for(int j=0;j<4;j++){
		int x=X[i]+dx[j],y=Y[i]+dy[j];
		int id=m2.fnd(x,y);if(id==-1)continue;
		if(!cid[find(i)])cid[find(i)]=blk[id];
		else if(cid[find(i)]!=blk[id])return true;
	}
	return false;
}
int dfn[M],low[M],tot,cut;
bool fir[M];
void Tarjan(int x,bool rt){
	dfn[x]=low[x]=++tot;
	int son=0;
	for(auto y:v[x]){
		if(!dfn[y])son++,Tarjan(y,false),low[x]=min(low[x],low[y]),cut+=(!rt&&dfn[x]<=low[y]&&fir[x]);
		else low[x]=min(low[x],dfn[y]);
	}
	if(rt&&son>=2&&fir[x])cut++;
}
bool checkcut(){
	if(n==1||m==1)return true;
	for(int i=1;i<=cnt;i++)if(!dfn[i])Tarjan(i,true);
	return cut>=1;
}
void read(int &x){
	x=0;
	char c=getchar();
	while(c>'9'||c<'0')c=getchar();
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
int mian(){
	read(n),read(m),read(c);
	m1.clear(),m2.clear();
	for(int i=1;i<=cnt;i++)dfn[i]=low[i]=blk[i]=0,v[i].clear();cnt=tot=cut=bcm=0;
	for(int i=1,x,y;i<=c;i++)read(X[i]),read(Y[i]),m1.ins(X[i],Y[i],i);
	if(checkimp())return -1;
	for(int i=1;i<=c;i++)for(int j=-2;j<=2;j++)for(int k=-2;k<=2;k++){
		int x=X[i]+j,y=Y[i]+k;
		if(x<1||y<1||x>n||y>m||m1.fnd(x,y)!=-1)continue;
		int tmp=m2.fnd(x,y);
		if(tmp!=-1){fir[tmp]|=(abs(j)<=1&&abs(k)<=1);continue;}
		m2.ins(x,y,++cnt),p[cnt]=make_pair(x,y);
		fir[cnt]=(abs(j)<=1&&abs(k)<=1);
	}
	for(int i=1;i<=cnt;i++)for(int j=0;j<4;j++){
		int x=p[i].first+dx[j],y=p[i].second+dy[j];
		int tmp=m2.fnd(x,y);
		if(tmp!=-1)v[i].push_back(tmp);
	}
	if(checkdisc())return 0;
	if(checkcut())return 1;
	return 2;
}
int main(){
//	freopen("P1173_1.in","r",stdin); 
	scanf("%d",&T);
	while(T--)printf("%d\n",mian());
	return 0;
}

posted @ 2021-04-02 23:02  Troverld  阅读(204)  评论(0编辑  收藏  举报