P1196 [NOI2002]银河英雄传说(带权并查集)

题意:

有n艘舰依次排序,每次将i及其身后的舰艇合并至j及其所有舰艇之后,每次询问i到j舰艇之间的距离,如果不在一列输出-1

思路:

单纯的合并与查询是否在一列操作比较简单,难的在于查询距离

首先我们需要三个数组fa[i],sum[i],dis[i]分别为i的父亲,i列所有的舰艇数,与其到其父亲的距离

可能有人会想i到其父亲的距离不都是1嘛,其实在路径压缩过程中,父亲会与实际的情况不符,虽然直接相连但是可能距离并不为1

现在考虑合并(i,j)操作,每次合并操作只要对第一艘舰艇进行修改就好了,分别修改

dis数组的修改直接等于sum[j],之后将sum[j]+=sum[i],并将sum[i]=0

在每次查询时都会进行路径压缩,因此dis[k](k为排在i之后的舰艇)虽然在合并时没有修改,但是会在路径压缩(查询父亲)时修改成到该列第一艘舰艇的距离

之后在利用前缀后的思想,(dis[i]-dis[j])-1即为连个舰艇之间的舰艇数了

#include
#include
#include
#include
 using namespace std;
 const int maxn=3e4+10;
 int fa[maxn],sum[maxn],dis[maxn];
 int find(int x)
 {
     if(fa[x]==x)    return x;
     int f1=fa[x],f2=find(fa[x]);
     dis[x]+=dis[f1];
     fa[x]=f2;
     return f2;
 }
 void uni(int x,int y)
 {
     int f1=find(x),f2=find(y);
     fa[f2]=f1;
     dis[f2]=sum[f1];
     sum[f1]+=sum[f2];
     sum[f2]=0;
 }
 int main()
 {
     int t,i,j;
     char op;
     scanf("%d",&t);
     for(int i=1;i<=maxn;i++){
         fa[i]=i;
         sum[i]=1;
     }
     while(t--){
         cin>>op>>i>>j;
         if(op=='M') uni(j,i);
         else{
             if(find(i)!=find(j))    cout<<-1<<endl;
             else{
                 cout<1<<endl;
             }
         }
     }
    return 0;
 }

 

你可能感兴趣的:(P1196 [NOI2002]银河英雄传说(带权并查集))