[USACO18OPEN] Multiplayer Moo (并查集+维护并查集技巧)
Posted guapisolo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[USACO18OPEN] Multiplayer Moo (并查集+维护并查集技巧)相关的知识,希望对你有一定的参考价值。
题目大意:给你一个N*N的棋盘,棋盘上每个点都有一个权值
第一问求一个权值形成的最大联通块中点的数量
第一问求两个权值共同形成的最大联通块中点的数量
提供一种并查集的做法:(感谢大佬们的题解)
第一问把所有相同权值的相邻的点用带权并查集合并一下就OK了
第二问,就需要一些骚操作了
我们的目的是把两个不同权值的所有联通块合并,再去看它们共同形成的最大联通块的大小
可以用一个结构体记录两个联通块之间的关系
分别是两个联通块的标号(即这个联通块构成的并查集的祖先节点)
以及这两个联通块的颜色
而为了简化后面的匹配过程,要把第一个块的颜色编号改成较小的,第二个块的颜色编号改成较大的
然后对这个结构体按颜色编号的首项排序,如果首项相同就按第二项排序
这么做的目的是,让 两个相同颜色的联通块之间的关系 形成一段连续的区间,这样我们就可以省去很多时间!!!
然后每次都按关系从前到后 去合并两个颜色,统计答案,再用几个数组把并查集还原回去就行了;
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #define N 255 5 #define maxn 1000010 6 using namespace std; 7 8 int n,cnt; 9 int a[N][N],fa[N*N],id[N][N],use[N*N],que[N*N],sum[N*N],sm[N*N]; 10 struct E{ 11 int x,y,c1,c2; 12 }e[N*N*4]; 13 int cmp(E s1,E s2) 14 { 15 if(s1.c1!=s2.c1) return s1.c1<s2.c1; 16 else return s1.c2<s2.c2; 17 } 18 19 void e_add(int xx,int yy,int cc1,int cc2) 20 { 21 cnt++; 22 if(cc1>cc2) swap(cc1,cc2),swap(xx,yy); 23 e[cnt].x=xx,e[cnt].c1=cc1; 24 e[cnt].y=yy,e[cnt].c2=cc2; 25 } 26 bool check(int x,int y) 27 { 28 if(x<1||y<1||x>n||y>n) return false; 29 else return true; 30 } 31 int find_fa(int x) 32 { 33 int fx=x; 34 while(fx!=fa[fx]) fx=fa[fx]; 35 while(x!=fx){ 36 int pre=fa[x]; 37 fa[x]=fx; 38 x=pre; 39 } 40 return x; 41 } 42 void mrg1(int x,int y) 43 { 44 x=find_fa(x),y=find_fa(y); 45 if(x!=y){ 46 fa[y]=x; 47 sum[x]+=sum[y]; 48 } 49 } 50 void mrg2(int x,int y) 51 { 52 x=find_fa(x),y=find_fa(y); 53 if(x!=y){ 54 fa[y]=x; 55 sm[x]+=sm[y]; 56 } 57 } 58 59 int main() 60 { 61 //freopen("data.in","r",stdin); 62 scanf("%d",&n); 63 for(int i=1;i<=n;i++) 64 for(int j=1;j<=n;j++) 65 { 66 scanf("%d",&a[i][j]); 67 id[i][j]=(i-1)*n+j; 68 } 69 for(int i=1;i<=n*n;i++) sum[i]=1,fa[i]=i; 70 for(int i=1;i<=n;i++) 71 for(int j=1;j<=n;j++) 72 { 73 if(check(i+1,j)&&a[i][j]==a[i+1][j]) 74 mrg1(id[i][j],id[i+1][j]); 75 if(check(i,j+1)&&a[i][j]==a[i][j+1]) 76 mrg1(id[i][j],id[i][j+1]); 77 } 78 for(int i=1;i<=n*n;i++) sm[i]=sum[i]; 79 for(int i=1;i<=n;i++) 80 for(int j=1;j<=n;j++) 81 { 82 if(check(i+1,j)&&a[i][j]!=a[i+1][j]) 83 e_add(find_fa(id[i][j]),find_fa(id[i+1][j]),a[i][j],a[i+1][j]); 84 if(check(i,j+1)&&a[i][j]!=a[i][j+1]) 85 e_add(find_fa(id[i][j]),find_fa(id[i][j+1]),a[i][j],a[i][j+1]); 86 } 87 sort(e+1,e+cnt+1,cmp); 88 int ct=0,ret=0; 89 for(int i=1;i<=n*n;i++) 90 ret=max(ret,sum[i]); 91 printf("%d ",ret); 92 ret=0; 93 for(int i=1;i<=cnt;i++,ct=0) 94 { 95 que[++ct]=e[i].x,que[++ct]=e[i].y; 96 use[e[i].x]=use[e[i].y]=1; 97 mrg2(e[i].x,e[i].y); //merge 98 while(e[i+1].c1==e[i].c1&&e[i+1].c2==e[i].c2){ 99 i++; 100 if(!use[e[i].x]) use[e[i].x]=1,que[++ct]=e[i].x; 101 if(!use[e[i].y]) use[e[i].y]=1,que[++ct]=e[i].y; 102 mrg2(e[i].x,e[i].y); 103 } 104 for(int j=1;j<=ct;j++) //calc 105 ret=max(ret,sm[que[j]]); 106 for(int j=1;j<=ct;j++) //clear 107 { 108 fa[que[j]]=que[j]; 109 sm[que[j]]=sum[que[j]]; 110 use[que[j]]=0; 111 } 112 } 113 printf("%d ",ret); 114 return 0; 115 }
以上是关于[USACO18OPEN] Multiplayer Moo (并查集+维护并查集技巧)的主要内容,如果未能解决你的问题,请参考以下文章
P4379 [USACO18OPEN]Lemonade Line
P4377 [USACO18OPEN]Talent Show
4579: [Usaco2016 Open]Closing the Farm