洛谷P1173:[NOI2016] 网格(tarjan、离散化)

14 篇文章 0 订阅
8 篇文章 0 订阅

解析

看起来很不码农但写起来其实还行的一道题。
主要也是因为我贺题解把所有的雷都避过去了

首先一个比较显然的结论是:通过堵角上的,答案不超过2。
所以本题只要把答案是-1,0,1,2的情况判出来即可。

-1是只有一个空位或只有两个相邻空位。
0是原图不连通。
1是原图存在割点。
其他都是2。

做完了?太天真了。

n , m ≤ 1 0 9 n,m\le 10^9 n,m109

那咋办?
注意到, c ≤ 1 0 5 c\le 10^5 c105,这张图非常稀疏,在空网格上暴力tarjan看起来非常的蠢。
那怎么办?
我们尝试对于每个蛐蛐,提取出它周围的格子,这样的节点数就变成了 O ( c ) O(c) O(c) 级别。(感觉有些像华容道那个题)
然后在新的图上tarjan就好了。

还要注意亿点点坑点:

  1. 提出的“周围一圈”应该是 5 × 5 5\times 5 5×5 而不是 3 × 3 3\times 3 3×3,同时只有距离最近蛐蛐曼哈顿距离为1的点是割点才算割点。
  2. 判联通的时候要把8联通的蛐蛐全找出来一起判。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define ok debug("OK\n")
using namespace std;

const int N=1e5+100;

const int mod=1333331;
const double inf=1e9;
inline ll read(){
  ll x(0),f(1);char c=getchar();
  while(!isdigit(c)) {if(c=='-')f=-1;c=getchar();}
  while(isdigit(c)) {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
  return x*f;
}

int n,m,c;
bool flag=0;

struct node{
  int to,nxt;
}p[N*25*4*2];
int fi[N*25],cnt;
inline void addline(int x,int y){
  p[++cnt]=(node){y,fi[x]};fi[x]=cnt;
}

struct Hash{
  node p[N*25];
  int fi[mod+1],tot,cnt;
  ll key[N*25];
  int id[N*25];
  void ins(int x,int y,int Id){
    ll w=1ll*x*(m+1)+y,o=w%mod;
    ++tot;id[tot]=Id;key[tot]=w;    
    p[++cnt]=(node){tot,fi[o]};fi[o]=cnt;
    //debug("add o=%lld fi=%d\n",o,fi[o]);
  }
  int find(int x,int y){
    ll w=1ll*x*(m+1)+y,o=w%mod;
    for(int i=fi[o];~i;i=p[i].nxt){
      //debug("o=%lld i=%d\n",o,i);
      //ok;
      if(key[p[i].to]==w) return id[p[i].to];
    }
    return 0;
  }
  void init(){
    tot=0;
    memset(fi,-1,sizeof(fi));cnt=-1;
  }
}mp;

int tot;

int x[N],y[N];
bool jd[N*25];
int d24x[25]={0,-2,-2,-2,-2,-2,-1,-1,-1,-1,-1,0,0,0,0,1,1,1,1,1,2,2,2,2,2},d24y[25]={0,-2,-1,0,1,2,-2,-1,0,1,2,-2,-1,1,2,-2,-1,0,1,2,-2,-1,0,1,2};
int d8x[9]={0,-1,-1,-1,0,0,1,1,1},d8y[9]={0,-1,0,1,-1,1,-1,0,1};
int d4x[5]={0,-1,0,0,1},d4y[5]={0,0,-1,1,0};

int cut;
int dfn[N*25],low[N*25],tim,bel[N*25],Id;
void tarjan(int x,int rt=0){
  dfn[x]=low[x]=++tim;bel[x]=Id;
  int son(0);
  for(int i=fi[x];~i;i=p[i].nxt){
    int to=p[i].to;
    //if(to<=c) continue;
    if(!dfn[to]){
      tarjan(to);
      son++;
      if(low[to]>=dfn[x]){
	cut+=jd[x];
      }
      low[x]=min(low[x],low[to]);
    }
    else low[x]=min(low[x],dfn[to]);
  }
  if(rt&&son==1) cut-=jd[x];
  return;
}

bool vis[N];
int nowId;
void dfs(int x,int y){
  int now=mp.find(x,y);
  vis[now]=1;
  for(int d=1;d<=8;d++){
    int nx=x+d8x[d],ny=y+d8y[d];
    if(nx<1||nx>n||ny<1||ny>m) continue;
    int to=mp.find(nx,ny);
    if(to>c){
      if(nowId==0) nowId=bel[to];
      else if(nowId!=bel[to]) nowId=-1;
    }
    else if(!vis[to]) dfs(nx,ny);    
  }
}

void clear(){
  mp.init();
  memset(fi,-1,sizeof(int)*(tot+1));cnt=-1;
  memset(jd,0,sizeof(int)*(tot+1));
  cut=tim=Id=0;
  memset(dfn,0,sizeof(int)*(tot+1));
  memset(low,0,sizeof(int)*(tot+1));
  memset(bel,0,sizeof(int)*(tot+1));
  memset(vis,0,sizeof(int)*(c+1));
  tot=0;
}

void work(){
  n=read();m=read();tot=c=read();
  for(int i=1;i<=c;i++){
    x[i]=read();y[i]=read();
    mp.ins(x[i],y[i],i);    
  }
  for(int i=1;i<=c;i++){
    for(int d=1;d<=24;d++){
      int nx=x[i]+d24x[d],ny=y[i]+d24y[d];
      if(nx<1||nx>n||ny<1||ny>m) continue;      
      if(!mp.find(nx,ny)){
	mp.ins(nx,ny,++tot);
	int x=tot;
	for(int d=1;d<=4;d++){
	  int nnx=nx+d4x[d],nny=ny+d4y[d];
	  int to=mp.find(nnx,nny);
	  if(to&&to>c){
	    //printf("d=%d (%d %d) -> (%d %d)\n",d,nx,ny,nnx,nny);
	    addline(x,to);addline(to,x);
	  }
	}
      }
      int to=mp.find(nx,ny);
      if(to<=c) continue;
      if(abs(nx-x[i])<=1&&abs(ny-y[i])<=1) jd[to]=1;
    }
  }
  if(flag) for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++) printf("%d ",mp.find(i,j));
    puts("");
  }
  for(int i=c+1;i<=tot;i++){
    if(!dfn[i]){
      ++Id;tarjan(i,1);
    }
  }
  if(1ll*n*m-c<2||(1ll*n*m-c==2&&(Id==1||n*m==2))){
    puts("-1");return;
  }
  for(int i=1;i<=c;i++){
    if(!vis[i]){
      nowId=0;
      dfs(x[i],y[i]);
      if(nowId==-1){
	puts("0");return;
      }
    }
  }
  if(cut||n==1||m==1) puts("1");
  else puts("2");
  return;
}

signed main(){
  #ifndef ONLINE_JUDGE
  freopen("a.in","r",stdin);
  freopen("a.out","w",stdout);
  #endif
  memset(fi,-1,sizeof(fi));cnt=-1;
  int T=read();
  while(T--){
    clear();
    work();
  }
  return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值