zoj 3366 Islands
并查集
选拔赛的题目。题意:如图所示是一些六边形的单元,一开始初始化所有的单元都是海洋,然后给你一个序列,就是一个单元坐标的序列。如果这个单元是个海洋,看能不能把它变成陆地,能变成陆地的条件是,如果它周围(也就是和它直接相连的那六个单元)已经是陆地,加入这块会使总陆地面积变大,但是面积有个限制值s,一块陆地的总面积不能超过s,如果加入这块不超过限制那么就加入,并且这个块海洋变成了陆地,如果超过了限制值那么这块单元要忽略,依然是海洋。如果是这块单元已经是陆地了,那么直接跳过。一整块陆地的总面积就是它拥有的单元数
要你输出最后有多少块大陆地,每一块的面积是多少,面积按升序输出
这里要注意,好像(1,1)和(3,2)已经是陆地(可知他们是两块分离的陆地)而且面积都是1,这时候要加入(2,2)的话,会把那两块陆地都连接起来成为一块陆地,此时这块陆地的面积就是3了。说到这里应该能理解为什么sample是2块陆地,面积分别是2,3了
解法:一整块陆地就是一个集合,我们用并查集来保存一个集合的关系。每当读到一个新的单元,扫描它周围6个单元,看他们是不是陆地,如果是海洋的话那么直接跳过,如果是陆地的,那么就要处理了,属于陆地,那么这块单元必定属于一个集合,找到这个集合的祖先,那么就可以知道这个集合的大小也就是整块大陆的面积。但是这里有个最重要的点,虽然周围有6个单元,但是一定是每个单元都是属于不同的集合吗?不是的!也就是说那6个单元可能之前已经是连成一块了,我就是因为这样wa了几次。
所以扫描了周围了6个单元,找到了他们的祖先,我们并不是要全部的祖先,而是要所有不同的祖先,相同的我们抛弃。
得到了这些不同的祖先,也就是得到了不同的集合,不同的大陆,现在我们把这些大陆的面积全部加起来,如果小于限制值,那么这块新读入的单元可以作为一个连接器,把所有这些大陆连接起来,变成一块新的大陆,面积无非是之前面积和+1
既然已经全部合并为一个新大陆了,那么记得修改之前的大陆的祖先,这些祖先要全部归属于这块新读入的单元,也就是说这块新单元将作为新大陆的祖先
为了加速,我们开辟一些数组记录一些值,免得每次都遍历
PS:注意一点,看图要仔细,奇数列和偶数列是不同的,他们计算周围6个相邻单元的方法是不同的,看图便知
经过多次修改,时间提高了好些,一举冲进了前十名,最后冲到了第一名
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define N 50010 #define V 500 const int xx[6]={0,0,-1,-1,1,1} , yy[6]={1,-1,1,0,1,0} , _yy[6]={1,-1,0,-1,0,-1}; int a[N],b[N],p[N],c[N] , num[2*V+10][2*V+10]; bool land[2*V+10][2*V+10]; int n,s,numn; int find(int x) { return x==p[x]?x:p[x]=find(p[x]); } int main() { while(scanf("%d%d",&n,&s)!=EOF) { numn=0; memset(land,false,sizeof(land)); scanf("%d%d",&a[0],&b[0]); land[a[0]+V][b[0]+V]=true; num[a[0]+V][b[0]+V]=0; p[0]=0; c[0]=1; numn=1; for(int i=1; i<n; i++) { int x,y,ta,tb; scanf("%d%d",&ta,&tb); if(land[ta+V][tb+V]) continue; //已经是陆地直接跳过 int sum,numr,r[10]; sum=0; numr=0; for(int k=0; k<6; k++) //枚举六个方向 { x=ta+xx[k]; y=(ta&1)?tb+yy[k]:tb+_yy[k]; //if(x<-V || x>V || y<-V || y>V) continue; //不合法的单元 if(!land[x+V][y+V]) continue; //海洋跳过 int m=num[x+V][y+V]; int tmp=find(m); int j; for(j=0; j<numr; j++) if(r[j]==tmp) break; if(j==numr) r[numr++]=tmp; } for(int k=0; k<numr; k++) sum += c[r[k]]; if(sum<s) //连接周围六个方向都不超过限制值 {//那么以该点为祖先,将六个方向的集合都合并到该点上 a[numn] = ta; b[numn] = tb; land[ta+V][tb+V] = true; num[ta+V][tb+V] = numn; p[numn] = numn; c[numn] = sum+1; for(int k=0; k<numr; k++) p[r[k]]=numn; numn++; } } int Count=0,ans[N]; for(int k=0; k<numn; k++) if(p[k]==k) ans[Count++]=c[k]; sort(ans,ans+Count); printf("%d\n",Count); for(int k=0; k<Count; k++) { printf("%d",ans[k]); if(k==Count-1) printf("\n"); else printf(" "); } } return 0; }