并查集

C. 星际争霸 [ Discussion ]
Description
楚继光将星域战场划分成30 000列,每列依次编号为1,2,…,30 000。之后,他把自己的战舰也依次编号为1,2,…,30 000,让第i号战舰处于第i列(i=1,2,…,30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,楚继光会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为Mij,含义为让第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。但李旭琳早已在战略上取得了主动。在交战中,她可以通过庞大的情报网络随时监听楚继光的舰队调动指令。在楚继光发布指令调动舰队的同时,李旭琳为了及时了解当前楚继光的战舰分布情况,也会发出一些询问指令:Cij。该指令意思是,询问电脑,楚继光的第i号战舰与第j号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。
作为一个资深的高级程序设计员,你被要求编写程序分析楚继光的指令,以及回答李旭琳的询问。

Input
第1行有1个整数T(1≤T≤500000),表示总共有T条指令。
以下有T行,每行有一条指令。指令有两种格式:

  1. Mij :i和j是两个整数(1≤i,j≤30000),表示指令涉及的战舰编号。该指令是李旭琳监听到的楚继光发布的舰队调动指令,并且保证第i号战舰与第j号战舰不在同一列。
  2. Cij :i和j是两个整数(1≤i,j≤30000),表示指令涉及的战舰编号。该指令是楚继光发布的询问指令。

Output
你的程序应当依次对输入的每一条指令进行分析和处理:
如果是楚继光发布的舰队调动指令,则表示舰队排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息;
如果是李旭琳发布的询问指令,你的程序要输出一行,仅包含一个整数,表示在同一列上,第i号战舰与第j号战舰之间布置的战舰数目。如果第i号战舰与第j号战舰当前不在同一列上,则输出-1。

Samples
Input Copy
10
M 5 2
M 1 5
M 4 5
M 3 5
C 2 4
C 5 4
C 4 1
C 4 3
C 4 3
C 3 5
Output
2
1
0
0
0
2

#include <bits/stdc++.h>
            using namespace std;
            typedef long long ll;
            priority_queue <string,vector<string>,less<string> > q;
            priority_queue <int,vector<int>,greater<int> > pp;
            typedef struct sty{
                string name;
                int n;
            }ss;
            int cmp(ss a,ss b)
            {
                return a.n<b.n;
            }
            int pan(int n)
            {
             for(int i=2;i<=sqrt(n);i++)
                if(n%i==0)
                return 0;

               return 1;

            }
            int pan1(int n)
            {
                int t=sqrt(n);
                if(t*t==n)
                    return 1;
                return 0;
            }
            int max1(int x,int y)
            {
                if(x>y)
                    return x;
                return y;
            }
            int min1(int x,int y)
            {
                if(x>y)
                    return y;
                return x;
            }
            const int maxn=1e6+1;
            ll pre[maxn],sz[maxn],rk[maxn];
            ss b[maxn];
            ll c[maxn];
        int n,m,v;
        int cnpo(int x){
	if(pre[x]==x) return x;

	int t=pre[x];
	pre[x]=cnpo(pre[x]);
	rk[x]+=rk[t];

	return pre[x];
}
int main(){
    int t,x,y;
    char b[2];
	scanf("%d",&t);
	for(int i=1;i<=40000;i++)
        pre[i] = i, sz[i]=1;

    for(int i=1;i<=t;i++)
        {

         scanf("%s%d%d",&b,&x,&y);
        if(b[0]=='M')
        {
		int dx = cnpo(x);
		int dy = cnpo(y);
		pre[dx] = dy;
		rk[dx]=sz[dy];
		sz[dy]+=sz[dx];

        }

        else{

		if(cnpo(x) == cnpo(y))

		 printf("%d\n",abs(rk[x]-rk[y])-1);
		else

            printf("-1\n");
	   }

	}
	return 0;

}
                    并查集之朋友圈

F. 朋友 Good Friend

小财带着你写的代码去找小金,小金看完以后高呼:妙啊!两人也终于和好。

这俩多年的好朋友突然想到同一个问题,他们想知道其他同学是不是和他们一样是好朋友。

他们展开了调查,得了x条信息,每条信息有两个数字,每个数字代表一个同学,表示这两同学是好朋友。

在统计的过程中,小财又发挥了他的本质,他进行了y次询问,每次询问两个数字,每个数字代表一个同学,通过已经统计过的信息是否可以判断两人是好朋友?

注意:因为每个同学都特别喜欢交朋友,如果满足 1 和 2 是好朋友,并且 1 和 3 是好朋友,那么 2 和 3 也会成为好朋友

小财的询问难住了正在统计的小金,小金只好来想你求助,请你帮助他回答小财询问。

Input
第一行包含两个整数 N,M ,表示共有 N 个同学和 M 个操作并且满足 x+y=M
接下来 M 行,每行包含三个整数 Zi,Ai,Bi

当 Zi=1,表示 Ai 和 Bi 是好朋友

当 Zi=2,输出 Ai 和 Bi 是否是好朋友,是的输出 Y ,否则输出 N

1<=M,N<=100000
Output
对于每一个 Zi=2 的操作,都有一行输出,每行包含一个大写字母,为 Y 或者 N

Samples
Input Copy
3 4
1 1 2
2 1 3
1 2 3
2 1 3
Output
N
Y
起初1 3 并不是好朋友,只有1 2 是好朋友,当2 3 成为好朋友时,1 3 也便成为了好朋友

新人一枚 看了好多博客才稍微悟了一点 得知这个叫做并查集;
至于怎么玩起来的呢 请看代码;

#include<bits/stdc++.h>

using  namespace std;
const int maxn=1e6;
typedef long long ll;

//const double e = 2.7182818284590452353602875;
//const double PI = 3.1415926535897932384626434;

int tsp[maxn];
int ucka(int);

//建立关系  一直将小号放在朋友位置;
void join(int n,int m)
{
    if(n>m)
      int temp=n,n=m,m=temp;

    int fn=ucka(n),fm=ucka(m);
    if(fn!=fm)
    {
        if(fm>fn)
        tsp[fm]=fn;
        else
        tsp[fn]=fm;
    }
}
//查找编号最小的朋友;
int ucka(int n)
{
    while(tsp[n]!=n)
    {
        n=tsp[n];
    }
    return n;
}
//压缩路径 使得朋友编号最小
int ucka1(int n)
{
    if(tsp[n]==n)
    return n;

    return tsp[n]=ucka1(tsp[n]);

}
int main()
{
   int N,M,x,y,z;
  for(int i=1;i<=100000;i++)//初始化处理
  {
      tsp[i]=i;
  }
  cin>>N>>M;
  while(M--)
  {
      cin>>x>>y>>z;
      if(x==1)
      {
          join(y,z);
      }
      else
      {
         if(ucka1(y)==ucka(z))
                printf("Y\n");
         else
            printf("N\n");
      }

  }

    return 0;
}

其实写完之和也是很懵,分享一下我看的博客;
希望大神看到可以给小弟我讲解一番;
https://blog.csdn.net/the_ZED/article/details/105126583

A. 亲戚 [ Discussion ]
Description
魔法世界的王族传承了几千年,时至今日已成为一个庞大的家族,要判断家族的两个人是否是亲戚,是很不容易的一件事,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

Input
第一行:三个整数n,m,p(n≤20000,m≤1000000,p≤1000000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1≤Mi,Mj≤N,表示Ai和Bi具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

Output
P行,每行一个"YES"或"NO"。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

Samples
Input Copy
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
Output
YES
YES
NO

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue <string,vector<string>,less<string> > q;
priority_queue <int,vector<int>,greater<int> > pp;
int cmp(string a,string b)
{
    return a+b>b+a;
}
const int maxn=2e5+7;
int p[maxn];

int cnpo(int x)
{
    if(p[x]==x)  return x;

    return p[x]=cnpo(p[x]);
}
void join(int x,int y)
{
    int dx=cnpo(x);
    int dy=cnpo(y);
    if(dx==dy)  return;
    if(dx<dy)
    p[y]=dx;
    else
        p[x]=y;

}
int main()
{
  int n;
  int k;
  int m;
  cin>>n>>m>>k;
  for (int i=0;i<=n;i++)  p[i]=i;
  for(int i=0;i<m;i++)
  {
      int x,y;cin>>x>>y;

      join(x,y);
  }
  for(int i=0;i<k;i++)
  {
      int x,y;
      cin>>x>>y;


      if(cnpo(x)==cnpo(y))
        cout<<"YES";
      else
        cout<<"NO";
        if(i!=k-1)
            cout<<endl;
  }

    return 0;
}

B. 无所不在的宗教 [ Discussion ]
Description
宗教是人类文明的一种普遍的思想传统,魔法世界几万年来先后诞生和兴亡更替的各种文明,大多都受到宗教的影响。因此,即使由天顶星人编造的创世神谎言被揭穿,魔法世界仍有为数众多的人群信仰着各种各样的宗教。
现在魔法学院有n(0<n≤50000)个学员,已知有m(0≤m≤n(n−1)/2)对宗教信仰相同的学生,请估算这n个学生中最少有多少种宗教信仰。

Input
输入数据有好几组,每一组以两个数字n,m开始,代表有n个学生,m对宗教信仰相同的学生,随后的m行中,每行包含两个数字i和j,表示学生i和学生j的宗教信仰相同。最后一行两个0代表输入结束。

Output
输出整数ans表示最少有多少种宗教信仰,注意ans前面加上Case和组数和“:”。

Samples
Input Copy
10 9
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
10 4
2 3
4 5
4 8
5 8
0 0
Output
Case 1: 1
Case 2: 7

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
priority_queue <string,vector<string>,less<string> > q;
priority_queue <int,vector<int>,greater<int> > pp;
int cmp(string a,string b)
{
    return a+b>b+a;
}
const int maxn=2e5+7;
int p[maxn];

int cnpo(int x)
{
    if(p[x]==x)  return x;

    return p[x]=cnpo(p[x]);
}
void join(int x,int y)
{
    int dx=cnpo(x);
    int dy=cnpo(y);
    if(dx==dy)  return;
    if(dx<dy)
    p[y]=dx;
    else
    p[x]=y;

}
int main()
{
  int n;
  int k;
  int m;
  int zh=1;
 while(cin>>n>>m)
 {
     if(n==0&&m==0)
        return 0;
     for (int i=0;i<=n;i++)  p[i]=i;
  for(int i=0;i<m;i++)
  {
      int x,y;cin>>x>>y;

      join(x,y);
  }
  int ans=0;
  for(int i=1;i<=n;i++)
  {
      if(cnpo(i)==i) ans++;
  }

  printf("Case %d: %d\n",zh++,ans);
 }

    return 0;
}
  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛郎恋刘娘,刘娘念牛郎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值