[NOI][CODEVS]1540 银河英雄传说 2002年NOI全国竞赛 并查集

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_19895789/article/details/71429370

题目描述 Description

公元五八一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。

    宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争。泰山压顶集团派宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集团点名将杨威利组织麾下三万艘战舰迎敌。

    杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在这次决战中,他将巴米利恩星域战场划分成30000列,每列依次编号为1, 2, …, 30000。之后,他把自己的战舰也依次编号为1, 2, …, 30000,让第i号战舰处于第i(i = 1, 2, …, 30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为M i j,含义为让第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。

    然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通过庞大的情报网络随时监听杨威利的舰队调动指令。

    在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战舰分布情况,也会发出一些询问指令:C i j。该指令意思是,询问电脑,杨威利的第i号战舰与第j号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。

    作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以及回答莱因哈特的询问。

    最终的决战已经展开,银河的历史又翻过了一页……

输入描述 Input Description

输入文件galaxy.in的第一行有一个整数T1<=T<=500,000),表示总共有T条指令。

以下有T行,每行有一条指令。指令有两种格式:

1.        M  i  j  ij是两个整数(1<=i , j<=30000),表示指令涉及的战舰编号。该指令是莱因哈特窃听到的杨威利发布的舰队调动指令,并且保证第i号战舰与第j号战舰不在同一列。

2.        C  i  j  ij是两个整数(1<=i , j<=30000),表示指令涉及的战舰编号。该指令是莱因哈特发布的询问指令。

输出描述 Output Description

输出文件为galaxy.out。你的程序应当依次对输入的每一条指令进行分析和处理:

如果是杨威利发布的舰队调动指令,则表示舰队排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息;

如果是莱因哈特发布的询问指令,你的程序要输出一行,仅包含一个整数,表示在同一列上,第i号战舰与第j号战舰之间布置的战舰数目。如果第i号战舰与第j号战舰当前不在同一列上,则输出-1

样例输入 Sample Input

4

M 2 3

C 1 2

M 2 4

C 4 2

样例输出 Sample Output

-1

1

数据范围及提示 Data Size & Hint

第一列

第二列

第三列

第四列

&hellip;&hellip;

初始时

1

2

3

4

&hellip;&hellip;

M 2 3

1

3

2

4

&hellip;&hellip;

C 1 2

1号战舰与2号战舰不在同一列,因此输出-1

M 2 4

1

4

3

2

&hellip;&hellip;

C 4 2

4号战舰与2号战舰之间仅布置了一艘战舰,编号为3,输出1


原题链接

这题要用到并查集,网上有很多学习资料了,如果并查集不会的话那这题基本没戏。。。。。。

这题的思路就是在并查集的基础上记录自己前面有多少人和这一列有多少人

这题一开始疯狂WA,找了很久没找到错的,上网一查,发现没有像我一样用循环查找的。都是用递归查找的

后来我照着网上的递归写法就AC了,我找了很久都没找到错在哪了。。。。还是学长告诉我的

这也是我用博客记录下来的原因。。。

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int father[30001],sum[30001],front[30001]={0};//father代表父亲是谁 sum代表这个列有多少人 front代表x前面有多少人 
int Find(int x)//查找+状态压缩+更新前面有多少人 
{
//	int a=x;	 //这部分是错误的 
//	while(father[x]!=x)
//		x=father[x];
//	while(a!=x)
//	{
//		front[a]+=front[father[a]];//因为这里只把父亲有多少人加上了,实际上要把前面所有的有多少人都加上 
//		int tmp=father[a];
//		father[a]=x;
//		a=tmp;
//	}
//	return x;
	int a=x;
	int count=front[x]; 
	while(father[x]!=x)
	{
		x=father[x];
		count+=front[x];
	}
	while(a!=x)
	{
		int tmpc=count;
		count-=front[a];
		front[a]=tmpc;
		int tmp=father[a];
		father[a]=x;
		a=tmp;
	}
	return x;
//	if(father[x]==x)//正确的,递归式写法 
//		return x;
//	int a=Find(father[x]);
//	front[x]+=front[father[x]];
//	return father[x]=a;
}
void Unico(int x,int y)//合并+更新前面多少人+更新整列有多少人 
{
	father[x]=y;
	front[x]+=sum[y];
	sum[y]+=sum[x];
}
int main()
{
	for(int i=1;i<=30000;i++)//初始当前父亲是自己 每列一个人 前面0个人 
	{
		father[i]=i;
		sum[i]=1;		
	}
	int t;
	cin>>t;
	while(t--)
	{
		int x,y;
		char c; 
		cin>>c>>x>>y;
		int fx=Find(x);
		int fy=Find(y);
		if(c=='M')
		{
			Unico(fx,fy);
		}
		else
		{
			if(fx!=fy)
				printf("%d\n",-1);
			else
				printf("%d\n",int(abs(front[x]-front[y])-1));//因为这里只引用了math包,所以要做int类型转换,不然怕一些版本支持不好(例如我本地的dev) 
		}
	}
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_19895789/article/details/71429370