[NOI2002] 银河英雄传说
题解
考点:带权并查集。
设 \(d[x]\) 表示 \(x\) 节点前面的战舰数量,\(s[x]\) 表示以 \(x\) 节点为首的那一列战舰的总数,合并操作如下:
void add(int x,int y)
{
int a=find(x),b=find(y);
fa[a]=b;
d[a]=s[b];
s[b]+=s[a];
s[a]=0;
}
把 \(a\) 接到 \(b\),\(a\) 前面的就是 \(s[b]\),然后战舰的总数加 \(s[a]\),而 \(a\) 又不作为头,所以清零。
查询操作:
int find(int x)
{
if(fa[x]==x)return x;
int t1=fa[x],t2=find(fa[x]);
fa[x]=t2;
d[x]+=d[t1];
return fa[x];
}
因为我们进行了路径压缩,设 \(t1\) 为 \(x\) 原来的父亲,\(t2\) 为 \(x\) 现在的父亲,则 \(d[t1]\) 已经计算正确,然后 \(d[x]\) 就加上 \(d[t1]\)。
Code:
#include<bits/stdc++.h>
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=30000;
int fa[N+100],s[N+100],d[N+100];
int find(int x)
{
if(fa[x]==x)return x;
int t1=fa[x],t2=find(fa[x]);
fa[x]=t2;
d[x]+=d[t1];
return fa[x];
}
void add(int x,int y)
{
int a=find(x),b=find(y);
fa[a]=b;
d[a]=s[b];
s[b]+=s[a];
s[a]=0;
}
int main()
{
for(int i=1;i<=N;i++)fa[i]=i,s[i]=1,d[i]=0;
int n;read(n);
for(int i=1,x,y;i<=n;i++)
{
char c;
cin>>c;read(x);read(y);
if(c=='M')add(x,y);
else
{
if(find(x)!=find(y))printf("-1\n");
else printf("%d\n",abs(d[x]-d[y])-1);
}
}
return 0;
}