题目在P1173 [NOI2016] 网格 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
跳蚤国王和蛐蛐国王在玩一个游戏。
他们在一个 nn 行 mm 列的网格上排兵布阵。其中的 cc 个格子中 (0 \leq c \leq n\cdot m)(0≤c≤n⋅m),每个格子有一只蛐蛐,其余的格子中,每个格子有一只跳蚤。
我们称占据的格子有公共边的两只跳蚤是相邻的。
我们称两只跳蚤是连通的,当且仅当这两只跳蚤相邻,或存在另一只跳蚤与这两只跳蚤都连通。
现在,蛐蛐国王希望,将某些(零个,一个或多个)跳蚤替换成蛐蛐,使得在此之后存在至少两只跳蚤不连通。
例如:图 11 描述了一个 n=4n=4,m=4m=4,c=2c=2 的情况。
这种情况下蛐蛐国王可以通过将第二行第二列,和第三行第三列的两只跳蚤替换为蛐蛐,从而达成他的希望,如右图所示。并且,不存在更优的方案,但是可能存在其他替换两只跳蚤的方案。
你需要首先判断蛐蛐国王的希望能否被达成。如果能够达成,你还需要最小化被替换的跳蚤的个数。
输入格式
每个输入文件包含多组数据。
输入文件的第一行只有一个整数 TT,表示数据的组数。
接下来依次输入 TT 组数据,每组数据的第一行包含三个整数 n, m, cn,m,c。
接下来 cc 行,每行包含两个整数 x, yx,y 表示第 xx 行,第 yy 列的格子被一个蛐蛐占据。每一组数据当中,同一个蛐蛐不会被多次描述。
输出格式
对于每一组数据依次输出一行答案。
如果这组数据中,蛐蛐国王的希望不能被达成,输出 -1−1。否则,输出被替换的跳蚤的个数的最小值。
输入输出样例
输入 #1复制
4 4 4 2 1 1 4 4 2 3 1 1 2 2 2 2 1 1 2 2 1 1 0
输出 #1复制
2 1 0 -1
说明/提示
样例解释
第一组数据就是问题描述中的例子。
对于第二组数据,可以将第二行第二列的一只跳蚤替换为蛐蛐,从而使得存在两只跳蚤不连通,并且不存在更优的方案。
对于第三组数据,最初已经存在两只跳蚤不连通,故不需要再进行替换。
对于第四组数据,由于最多只有一只跳蚤,所以无论如何替换都不能存在两只跳蚤不连通。
数据范围
对于全部的测试点,保证 1 \leq T \leq 201≤T≤20。我们记 \sum c∑c 为某个测试点中,其 TT 组输入数据的所有 cc 的总和。对于所有的测试点,\sum c \leq 10^5∑c≤105。
对于全部的数据,满足 1 \leq n,m \leq 10^91≤n,m≤109,0 \leq c \leq n \times m0≤c≤n×m,1 \leq x \leq n, 1 \leq y \leq m1≤x≤n,1≤y≤m。
每个测试点的详细数据范围见下表。表中的 n,m,cn,m,c 均是对于单个输入数据(而非测试点)而言的,也就是说同一个测试点下的 TT 组数据均满足限制条件;而 \sum c∑c是对于单个测试点而言的。为了方便阅读,“测试点”一列被放到了表格的中间而不是左边。
n,mn,m | 测试点 | cc |
---|---|---|
n*m\leq 4n∗m≤4 | 11 | c\leq n*mc≤n∗m |
n*m\leq 8n∗m≤8 | 22 | c\leq n*mc≤n∗m |
n*m\leq 15n∗m≤15 | 33 | c\leq n*mc≤n∗m |
n*m\leq 30n∗m≤30 | 44 | c\leq n*mc≤n∗m |
n*m\leq 100n∗m≤100 | 55 | c\leq n*mc≤n∗m |
n*m\leq 300n∗m≤300 | 66 | c\leq n*mc≤n∗m |
n*m\leq 10^3n∗m≤103 | 77 | c\leq n*mc≤n∗m |
n*m\leq 2\times 10^4n∗m≤2×104 | 88 | c\leq 5c≤5 |
n*m\leq 2\times 10^4n∗m≤2×104 | 99 | c\leq 15c≤15 |
n*m\leq 2\times 10^4n∗m≤2×104 | 1010 | c\leq 30c≤30 |
n,m\leq 2\times 10^4,n*m\leq2\times 10^4n,m≤2×104,n∗m≤2×104 | 1111 | \sum c\leq 2\times 10^4∑c≤2×104 |
n,m\leq 2\times 10^4,n*m\leq10^5n,m≤2×104,n∗m≤105 | 1212 | \sum c\leq 2\times 10^4∑c≤2×104 |
n,m\leq 2\times 10^4,n*m\leq3\times 10^5n,m≤2×104,n∗m≤3×105 | 1313 | \sum c\leq 2\times 10^4∑c≤2×104 |
n,m\leq 2\times 10^4,n*m\leq10^6n,m≤2×104,n∗m≤106 | 1414 | \sum c\leq 2\times 10^4∑c≤2×104 |
n,m\leq 2\times 10^4,n*m\leq 10^9n,m≤2×104,n∗m≤109 | 1515 | \sum c\leq 2\times 10^4∑c≤2×104 |
n,m\leq 10^5n,m≤105 | 1616 | \sum c\leq 10^5∑c≤105 |
n,m\leq 10^9n,m≤109 | 1717 | c=0c=0 |
n,m\leq 10^9n,m≤109 | 1818 | c\leq 1c≤1 |
n,m\leq 10^9n,m≤109 | 1919 | c\leq 2c≤2 |
n,m\leq 10^9n,m≤109 | 2020 | c\leq 3c≤3 |
n,m\leq 10^9n,m≤109 | 2121 | c\leq 10c≤10 |
n,m\leq 10^9n,m≤109 | 2222 | c\leq 30c≤30 |
n,m\leq 10^9n,m≤109 | 2323 | c\leq 300c≤300 |
n,m\leq 10^9n,m≤109 | 2424 | \sum c\leq 2 \times 10^4∑c≤2×104 |
n,m\leq 10^9n,m≤109 | 2525 | \sum c\leq 10^5∑c≤105 |
题解
一个显而易见的性质是答案不大于2(角上的跳蚤最多与两个相邻)
继而发现答案其实只可能是-1,0,1,2~~(废话)~~,分类讨论:
-1:nm-c<2nm−c<2或者nm-c=2nm−c=2且剩下的两只跳蚤相邻;
0:原图不连通;
1:原图联通且有割顶;
否则为2。
但n,m\le 10^9n,m≤109这种数据范围肯定不能直接做了,发现只有蚱蜢周围的跳蚤可能是割顶,是不是可以把它们选出来建图呢?
hack:(以下均用#表示蚱蜢,*表示选出的跳蚤,.表示其余跳蚤)
.**
.*#
.**
中间的*成为假的割顶。因此,我们要向外扩展两圈,然后如果在第一圈内发现割顶,那么才真正发现了割顶。这个另外的题解也有讲,这里就不仔细说了。
但是判联通性也有许多大坑,但许多题解都没提,我来提一下或许是我太蒟了
首先是怎么判。不能直接在建好的图上判,举个栗子:
#**.**#
会被判为不连通。而真正的不连通具有什么性质呢?我们发现,障碍点(蚱蜢)会把它周围的点分成至少两个联通块,由此得到算法1:
对选出的结点进行四联通floodfill,检查每个障碍周围的点是否均属于同一个联通块。
这个方法已经可以通过官方数据了~~,但我们还有万恶的UOJ~~
我们发现这个方法是可以hack的:
*###*
每个结点周围只有一个联通块,联通,~~于是你WA的一声哭了出来,~~所以,我们必须将连在一起的障碍一起处理,得到算法2:
对选出的结点进行四联通floodfill,对障碍进行八连通floodfill,检查每个障碍块周围的点是否均属于同一个联通块。
注意,对结点的检查不能判重,比如下面的例子:
***
###
###
###
***
*#*
如果先检查了下面的#,再在检查上面的障碍块使省略重复访问,那么将返回联通。
这就没问题了。
至于实现么,反正map会被卡,hash又不难打,当然选择hash
代码
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};
const int P=1000117;
const int N=100050;
char rB[1<<21],*rS,*rT;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline int rd(){
char c=gc();
while(c<48||c>57)c=gc();
int x=c&15;
for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
return x;
}
int x[N],y[N],G[N*24],to[N*192],nxt[N*192],sz,cnt,pre[N*24],dfsc,n,m,c,tmpx[N*24],tmpy[N*24],ctmp;
bool isok[N*24],iscut[N*24];
struct node{
int x,y;
node(){}
node(int x,int y):x(x),y(y){}
};
queue<node> Q,q;
struct Hash{ //用hash实现map
int h[P],vx[N*25],vy[N*25],p[N*25],nxt[N*25],sz;
inline void clear(){
memset(h,0,sizeof(h));sz=0;
}
inline void ins(int x,int y,int id){
int pos=((ll)(x-1)*n+y-1)%P;
vx[++sz]=x;vy[sz]=y;p[sz]=id;nxt[sz]=h[pos];h[pos]=sz;
}
inline int ask(int x,int y){
for(int k=h[((ll)(x-1)*n+y-1)%P];k;k=nxt[k])if(vx[k]==x&&vy[k]==y)return p[k];
return 0;
}
}h,col,tem;
inline int Abs(int x){return x<0?-x:x;}
inline int Max(int a,int b){return a>b?a:b;}
inline void add(int u,int v){
to[++sz]=v;nxt[sz]=G[u];G[u]=sz;
to[++sz]=u;nxt[sz]=G[v];G[v]=sz;
}
inline bool check(){
int i,j,k,tx,ty;
for(i=1;i<=n;++i)
for(j=1;j<=m;++j)if(!h.ask(i,j)){
for(k=0;k<4;++k)if((tx=i+dx[k])&&tx<=n&&(ty=j+dy[k])&&ty<=m&&!h.ask(tx,ty))return 1;
return 0;
}
}
inline void bfs(int sx,int sy,int cl){ //第一次floodfill
int i,u,v,tx,ty;
q.push(node(sx,sy));col.ins(sx,sy,cl);
while(!q.empty()){
u=q.front().x;v=q.front().y;q.pop();
for(i=0;i<4;++i)if((tx=u+dx[i])&&tx<=n&&(ty=v+dy[i])&&ty<=m&&h.ask(tx,ty)>0&&!col.ask(tx,ty)){
col.ins(tx,ty,cl); //用col来记录所属联通块编号(也成为颜色)
q.push(node(tx,ty));
}
}
}
inline bool bfs2(int sx,int sy){
int i,u,v,x,y,t;
q.push(node(sx,sy));tem.ins(sx,sy,-1);
while(!q.empty()){
u=q.front().x;v=q.front().y;q.pop();
for(x=Max(1,u-1);x<=n&&x<=u+1;++x)
for(y=Max(1,v-1);y<=m&&y<=v+1;++y)if((t=h.ask(x,y))&&!tem.ask(x,y))if(t==-1){
tem.ins(x,y,-1); //用tem来防止对障碍结点重复访问
//对跳蚤结点的重复访问最多总共c*8个,不会影响复杂度
q.push(node(x,y));
}else{tmpx[++ctmp]=x;tmpy[ctmp]=y;}
}
if(ctmp==-1)return 1;
for(i=1,t=col.ask(tmpx[0],tmpy[0]);i<=ctmp;++i)if(col.ask(tmpx[i],tmpy[i])!=t)return 0;
return 1;
}
inline bool ncon(){ //判断是否不连通
int i,u,v,ccl=0;
col.clear();
while(!Q.empty()){
u=Q.front().x;v=Q.front().y;Q.pop();
if(col.ask(u,v))continue;
bfs(u,v,++ccl);
}
tem.clear();
for(i=0;i<c;++i)if(!tem.ask(x[i],y[i])){
ctmp=-1;
if(!bfs2(x[i],y[i]))return 1;
}
return 0;
}
int dfs(int u,int fa){ //dfs求割顶
int i,v,lowu=pre[u]=++dfsc,lowv,chd=0;
for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa)if(!pre[v]){
++chd;
if((lowv=dfs(v,u))>=pre[u])iscut[u]=1;
if(lowv<lowu)lowu=lowv;
}else if(pre[v]<lowu)lowu=pre[v];
if(!fa&&chd==1)iscut[u]=0;
return lowu;
}
int main(){
int T=rd(),i,j,k,l,t,tt,tx,ty;
bool ok;
while(T--){
n=rd();m=rd();c=rd();
h.clear();
for(i=0;i<c;++i){
x[i]=rd();y[i]=rd();
h.ins(x[i],y[i],-1);
}
if((ll)n*m-c<2ll){
puts("-1");
continue;
}
if((ll)n*m-c==2ll){
puts(check()?"-1":"0");
continue;
}
memset(G,0,sizeof(G));ok=sz=cnt=dfsc=0;
memset(pre,0,sizeof(pre));
memset(iscut,0,sizeof(iscut));
memset(isok,0,sizeof(isok));
//建图
for(i=0;i<c;++i)
for(j=Max(1,x[i]-2);j<=x[i]+2&&j<=n;++j)
for(k=Max(1,y[i]-2);k<=y[i]+2&&k<=m;++k)if(!(t=h.ask(j,k))){
h.ins(j,k,++cnt);Q.push(node(j,k));
isok[cnt]=Max(Abs(j-x[i]),Abs(k-y[i]))<=1;
for(l=0;l<4;++l)if((tx=j+dx[l])&&tx<=n&&(ty=k+dy[l])&&ty<=m&&(tt=h.ask(tx,ty))>0)add(cnt,tt);
}else if(t>0&&Max(Abs(j-x[i]),Abs(k-y[i]))<=1)isok[t]=1;
if(ncon()){
puts("0");
continue;
}
if(n==1||m==1){ //一行或一列可以特判
puts("1");
continue;
}
for(i=1;i<=cnt;++i){
if(!pre[i])dfs(i,0);
if(isok[i]&&iscut[i]){
puts("1");
ok=1;break;
}
}
if(!ok)puts("2");
}
return 0;
}