[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 (并查集+维护并查集技巧)的主要内容,如果未能解决你的问题,请参考以下文章

[USACO18OPEN]Talent Show

P4379 [USACO18OPEN]Lemonade Line

P4377 [USACO18OPEN]Talent Show

4579: [Usaco2016 Open]Closing the Farm

CF1081G Mergesort Strikes Back

洛谷 P3147 [USACO16OPEN]262144