Description
为了封印辉之环,古代塞姆利亚大陆的人民在异空间中建造了一座设备塔。
简单的说,这座设备塔是一个漂浮在异空间中的圆柱体,圆柱体两头的圆是计算核心,而侧面则是
传输信息所用的数据通道,划分成
N∗m
个区块。
然而,随着工作的继续进行,他们希望把侧面的一部分区块也改造成其他模块。然而,任何时候都
必须保证存在一条数据通道,能从圆柱体的一端通向另一端。
由于无法使用辉之环掌控下的计算系统,他们寻求你的帮助来解决这个问题。他们将逐个输入想要
改造的区域,而你则执行所有可行的改造并忽略可能导致数据中断的改造。
Input
第一行,包含两个整数
N;M;K
,表示侧面的长和宽,以及操作数。
接下来K 行,每行包含三个整数
xi;yi
,表示操作的区块的坐标。
数据保证不会对已经操作成功的区块进行操作。
Output
输出一行,表示有多少个操作可以被执行。
Sample Input
3 4 9
2 2
3 2
2 3
3 4
3 1
1 3
2 1
1 1
1 4
Sample Output
6
Data Constraint
• 对于分值为30 的子任务1,保证
N;M<=100;K<=5000
• 对于分值为30 的子任务2,保证
N;M<=3000;K<=5000
• 对于分值为40 的子任务3,保证
N;M<=3000;K<=300000
。
Hint
Solution
这道题是一道神奇的模拟题!!!
很容易想到的普通暴力枚举在这个超大范围下也无能为力~
这里有一个利用并查集的巧妙方法
首先,注意到路径可以横跨左右边界
所以,把这个图向右复制一份,即 长*2 ,点都双份处理
每一个点(编号排成一列地处理)与八连通的点进行并查集
那么如何判断加点时,所构成的联通快有没有横向封盖呢?
于是设一个标记数组,记号可以设为数据编号这种独一无二的号码
将要加入的点在左边地图里的八连通点,这些点的父亲编号在标记数组里赋值
然后在右边地图里的八连通点的父亲编号在标记数组里查询
如果已赋值,说明加入的话会构成横向封盖,则不能加入!
这样,我们就能方便的判断,省去了在并查集数组里加加删删的麻烦!
这样巧妙地运用并查集,使时间复杂度降到了 O(K) 再加一些常数!!!
Code
#include<cstdio>
using namespace std;
const int N=3001,M=2*N*N;
const int way[8][2]={{0,1},{1,0},{0,-1},{-1,0},
{1,1},{-1,1},{-1,-1},{1,-1}};
int n,m,cnt,ans;
int f[M],g[M];
bool bz[M];
bool pd;
inline int read()
{
int data=0; char ch=0;
while(ch<'0' || ch>'9') ch=getchar();
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data;
}
inline int get(int x,int y){return (x-1)*2*m+y;}
inline int find(int x){return (f[x]==x)?x:f[x]=find(f[x]);}
inline void work(int x,int y)
{
int p=get(x,y);
bz[p]=true;
for(int i=0;i<8;i++)
{
int xx=x+way[i][0],yy=y+way[i][1];
if(!xx || xx>n) continue;
if(!yy) yy+=2*m; else if(yy>2*m) yy-=2*m;
int q=get(xx,yy);
if(bz[q])
{
int f1=find(p),f2=find(q);
if(f1!=f2) f[f1]=f2;
}
}
}
int main()
{
n=read(),m=read(),cnt=get(n,2*m);
for(int i=1;i<=cnt;i++) f[i]=i;
int k=read()+1;
while(--k)
{
int x=read(),y=read();
for(int i=0;i<8;i++)
{
int xx=x+way[i][0],yy=y+way[i][1];
if(!xx || xx>n) continue;
if(!yy) yy+=2*m; else if(yy>2*m) yy-=2*m;
int p=get(xx,yy);
if(bz[p]) g[find(p)]=k;
}
for(int i=pd=0;i<8;i++)
{
int xx=x+way[i][0],yy=y+m+way[i][1];
if(!xx || xx>n) continue;
if(!yy) yy+=2*m; else if(yy>2*m) yy-=2*m;
int p=get(xx,yy);
if(bz[p] && g[find(p)]==k){pd=true;break;}
}
if(!pd)
{
ans++;
work(x,y);
work(x,y+m);
}
}
printf("%d",ans);
return 0;
}