CF1380E.Merging Towers(并查集)

/*
 *1380E.Merging Towers
 *给出半径为1~n的n个盘子和m个塔,要求每个塔上盘子的半径始终从底向上递减
 *一次操作可以将一个塔上的任意个盘子移动到另一个塔的顶部。
 *令某一情形下的复杂度为将所有盘子移动到同一个塔上所需的最小操作数。
 *题目给出m-1次询问,每次询问时输出当前情形的复杂度并合并到两个塔的盘子到同一个塔上
 *考虑如果一个一个搬碟子,答案是n-1。
 *如果有两个相邻的碟子,就把他们看成一个整体,答案可以减去1.
 *按照每堆的堆顶建树,每次合并的时候,对于小树的节点我们都判断它在原数组的相邻节点的祖先节点是不是合并的大树top点
 *如果是,既然相邻那么他们的半径就是差值最小的,也就是说可以把他们看成一个整体 
  */
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
vector<int> g[maxn];
int father[maxn];
int a[maxn];
int findfather (int x) {
    int a=x;
    while (x!=father[x]) x=father[x];
    while (a!=father[a]) {
        int z=a;
        a=father[a];
        father[z]=x;
    }
    return x;
}  
int main () {
    int n,m;
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        g[a[i]].push_back(i);
    }
    int wjm=n;
    for (int i=1;i<=m;i++) father[i]=i;
    for (int i=2;i<=n;i++) if (a[i-1]==a[i]) wjm--;
    printf("%d
",wjm-1);
    for (int i=1;i<m;i++) {
        int x,y;
        scanf("%d%d",&x,&y);
        int faX=findfather(x);
        int faY=findfather(y);
        if (g[faX].size()>g[faY].size()) swap(faX,faY);
        for (int j:g[faX]) {
            if (j+1<=n&&a[j+1]==faY) wjm--;
            if (j-1>=1&&a[j-1]==faY) wjm--;
            g[faY].push_back(j);
          }
          for (int j:g[faX]) a[j]=faY;
          father[faX]=faY;
          printf("%d
",wjm-1);
    } 
}
原文地址:https://www.cnblogs.com/zhanglichen/p/13337008.html