[NOI2016] 网格
XXXVIII.[NOI2016] 网格
首先,答案一定 ,因为四个角的跳蚤被围住只需要两个蛐蛐,而如果蛐蛐占住了一个角又会产生新的角。
的情形比较容易,要么空隙少于 个,要么仅剩的两个空隙在一起。两种情况下 都与 同级别,因此可以暴力。
的情形等价于存在不连通的部分。可以发现,蛐蛐形成的八连通块,找到块中所有节点周边八个跳蚤并跑跳蚤的四联通性。若发现八连通块周边的跳蚤属于两个及以上不同的连通块,则即为 。
的情形等价于存在割点。割点有两种可能,一是 中至少有一个为 ,二是蛐蛐把跳蚤分出了割点。假如依旧只找周边 个跳蚤跑割点的话,那周边一圈的节点可能会被当成割点,比如这个样例,其中 #
代表蛐蛐。
...
...
..#
...
...
但是我们发现,只需要选取周边两圈的节点即可规避上述问题。但是,需要注意的是,割点只能统计周边一圈的节点,否则会被
#....
.....
.....
.....
....#
这组样例干掉——正中央的那个节点是割点,但不是内圈割点,因此不能计入。
然后剩余的情景即为 。
需要注意的是,本题数据范围很大,存图要么用 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?