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行,每行有一条指令。指令有两种格式:
- Mij :i和j是两个整数(1≤i,j≤30000),表示指令涉及的战舰编号。该指令是李旭琳监听到的楚继光发布的舰队调动指令,并且保证第i号战舰与第j号战舰不在同一列。
- 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;
}