[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;
}
posted @ 2022-07-06 11:22  Epoch_L  阅读(40)  评论(0编辑  收藏  举报