您的位置:首页 > 其它

ssoj2385path

2015-08-23 09:37 148 查看

题目描述

有一个r*c的地图,把左边界和右边界粘起来使得形成一个圆柱,现在要不断地挖去其中的格子,要求任何时候都存在一条从最上方到最下方的路径(四联通),如果某次操作不满足要求则不做,问最后有多少次操作是成功的。

输入

第一行三个数r,c,n,空格分隔。

接下来n行,每行两个数x,y,表示要删除的格子在x行y列。

输出

只一行,一个数,表示成功的删除操作的数量。

样例输入

3 4 92 23 22 33 43 11 32 11 11 4

样例输出

6

提示

30%的数据,n<=5000。

100%的数据,r,c<=3000,n<=300000。

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=3003;
const int dx[8]={1,1,0,-1,-1,-1,0,1};
const int dy[8]={0,1,1,1,0,-1,-1,-1};
int r,c,n,fa[maxn*200],vis[maxn][maxn],ans=0,cnt,pos[maxn*200];
inline int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
inline int find(int x){
if(fa[x]==x)return fa[x];
else return (fa[x]=find(fa[x]));
}
inline bool noway(int x,int y){
int u,v;
if(c==1)return false;
++cnt;
for(int i=1;i<=2;++i){
if(i&1)u=x,v=y;
else u=x,v=y+c;
for(int k=0;k<8;++k){
int uu=u+dx[k];
int vv=v+dy[k];
if(uu>r || uu<=0)continue;
if(vv<=0)vv+=2*c;
if(vv>2*c)vv-=2*c;
if(vis[uu][vv]){
if(i&1)pos[find(vis[uu][vv])]=cnt;
else if(pos[find(vis[uu][vv])]==cnt) return false;
}
}
}
return true;
}
int main(){
memset(vis,0,sizeof(vis));
memset(pos,0,sizeof(pos));
r=get();c=get();n=get();
for(int i=1;i<=n*2;++i)fa[i]=i;
for(int i=1;i<=n;++i){
int x=get(),y=get();
if(noway(x,y)){
int u,v,uu,vv;
vis[x][y]=i;
vis[x][y+c]=i+n;
for(int k=1;k<=2;++k){
if(k&1)u=x,v=y;
else u=x,v=y+c;
for(int j=0;j<8;++j){
uu=u+dx[j];
vv=v+dy[j];
if(uu>r || uu<=0)continue;
if(vv<=0)vv+=2*c;
if(vv>2*c)vv-=2*c;
if(vis[uu][vv])fa[find(vis[u][v])]=find(fa[vis[uu][vv]]);
}
}
++ans;
}
}
printf("%d\n",ans);
return 0;
}


思路:容易发现,当无法从顶到底时,当且仅当图中有横向通道被截,因此可以用并查集记录与每个被挖格子相连的最早被挖的格子,宽度置为两倍,一边放,一边走,若是两边都能走到同一父亲节点,说明横向被截。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: