文章目录
序言
今天老师开始教我们并查集。我靠,贼水,虽然错的多。还TLE了五个点,不过洛谷的这题数据范围贼小 洛谷的数据:
n , m , p n , m , p , ( n , m , p ≤ 5000 n , m , p ≤ 5000 ) n,m,pn,m,p,(n,m,p \le 5000n,m,p≤5000) n,m,pn,m,p,(n,m,p≤5000n,m,p≤5000)
我们的数据:
输入由两部分组成。
第一部分以 N , M N,M N,M开始。 N N N为问题涉及的人的个数 ( 1 ≤ N ≤ 20000 ) (1≤N≤20000) (1≤N≤20000)。这些人的编号为 1 , 2 , 3 , … , N 1,2,3,…, N 1,2,3,…,N。下面有M行 ( 1 ≤ M ≤ 1000000 ) (1≤M≤1 000 000) (1≤M≤1000000),每行有两个数 a i , b i a_i, b_i ai,bi,表示已知 a i a_i ai和 b i b_i bi是亲戚。
第二部分以 Q Q Q开始。以下 Q Q Q行有 Q Q Q个询问 ( 1 ≤ Q ≤ 1000000 ) (1≤Q≤1 000 000) (1≤Q≤1000000),每行为 c i , d i c_i, d_i ci,di,表示询问 c i c_i ci和 d i d_i di是否为亲戚。
正文
亲戚
时间限制: 1000 ms 空间限制: 262144 KB
题目描述
或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。
输入
输入由两部分组成。
第一部分以N,M开始。N为问题涉及的人的个数(1≤N≤20000)。这些人的编号为1,2,3,…, N。下面有M行(1≤M≤1 000 000),每行有两个数ai, bi,表示已知ai和bi是亲戚。
第二部分以Q开始。以下Q行有Q个询问(1≤Q≤1 000 000),每行为ci, di,表示询问ci和di是否为亲戚。
输出
对于每个询问ci, di,输出一行:若ci和di为亲戚,则输出“Yes”,否则输出“No”。
样例输入
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
样例输出
Yes
No
Yes
讲解
呃…一康见题就明白这是标准并查集啊,所以要打标准读入等东西
如果自学的话,我推荐这篇这篇,他讲的很好
讲回代码,我靠,我因为join用的是int导致运行错误,这个编译器好拉呀
最后还是海笑帮我改的
int rel[20001];//并查集数组
int relative(int x) {//路径压缩
return (rel[x]!=x)?(rel[x]=relative(rel[x])):x;//这个三目可能有人康不下去,建议拆开成if
}
int check(int x ,int y) {//判断是否在一个家里
return (relative(x)==relative(y))?1:0;
}
void join(int x,int y) {//加为亲戚
x=relative(x),y=relative(y);
rel[y]=x;
}
Wrong Answer Code
#include<bits/stdc++.h>
using namespace std;
int rel[20001];
int relative(int x) {
return (rel[x]!=x)?(rel[x]=relative(rel[x])):x;
}
int check(int x ,int y) {
return (relative(x)==relative(y))?1:0;
}
int join(int x,int y) {
x=relative(x),y=relative(y);
rel[y]=x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie();
int n,m,x,y,j;
cin>>n>>m;
for(int i=1; i<=n; i++) rel[i]=i;
for(int i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
join(x,y);
}
scanf("%d",&j);
for(int i=1; i<=j; i++) {
scanf("%d%d",&x,&y);
if(check(x,y)) cout<<"Yes\n";
else cout<<"No\n";
}
}
Accepted Code
#include<bits/stdc++.h>
using namespace std;
int rel[20001];
int relative(int x) {
return (rel[x]!=x)?(rel[x]=relative(rel[x])):x;
}
int check(int x ,int y) {
return (relative(x)==relative(y))?1:0;
}
void join(int x,int y) {
x=relative(x),y=relative(y);
rel[y]=x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie();
int n,m,x,y,j;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) rel[i]=i;
for(int i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
join(x,y);
}
scanf("%d",&j);
for(int i=1; i<=j; i++) {
scanf("%d%d",&x,&y);
if(check(x,y)) cout<<"Yes\n";
else cout<<"No\n";
}
}
洛谷亲戚
题目背景
若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
题目描述
规定: x x x 和 y y y 是亲戚, y y y 和 z z z 是亲戚,那么 x x x 和 z z z 也是亲戚。如果 x x x, y y y 是亲戚,那么 x x x 的亲戚都是 y y y 的亲戚, y y y 的亲戚也都是 x x x 的亲戚。
输入格式
第一行:三个整数 n , m , p n,m,p n,m,p,( n , m , p ≤ 5000 n,m,p \le 5000 n,m,p≤5000),分别表示有 n n n 个人, m m m 个亲戚关系,询问 p p p 对亲戚关系。
以下 m m m 行:每行两个数 M i M_i Mi, M j M_j Mj, 1 ≤ M i , M j ≤ N 1 \le M_i,~M_j\le N 1≤Mi, Mj≤N,表示 M i M_i Mi 和 M j M_j Mj 具有亲戚关系。
接下来 p p p 行:每行两个数 P i , P j P_i,P_j Pi,Pj,询问 P i P_i Pi 和 P j P_j Pj 是否具有亲戚关系。
输出格式
p
p
p 行,每行一个 Yes
或 No
。表示第
i
i
i 个询问的答案为“具有”或“不具有”亲戚关系。
样例
样例输入
6 5 3
1 2
1 5
3 4
5 2
1 3
1 4
2 3
5 6
样例输出
Yes
Yes
No
解释
这个和上面差不多,只是读入不一样了
Accepted Code
#include<bits/stdc++.h>
using namespace std;
int rel[20001];
int relative(int x) {
return (rel[x]!=x)?(rel[x]=relative(rel[x])):x;
}
int check(int x ,int y) {
return (relative(x)==relative(y))?1:0;
}
void join(int x,int y) {
x=relative(x),y=relative(y);
rel[y]=x;
}
int main() {
ios::sync_with_stdio(false);
cin.tie();
int n,m,x,y,j;
scanf("%d%d",&n,&m);
scanf("%d",&j);
for(int i=1; i<=n; i++) rel[i]=i;
for(int i=1; i<=m; i++) {
scanf("%d%d",&x,&y);
join(x,y);
}
for(int i=1; i<=j; i++) {
scanf("%d%d",&x,&y);
if(check(x,y)) cout<<"Yes\n";
else cout<<"No\n";
}
}
格子游戏
时间限制: 1000 ms 空间限制: 262144 KB
题目描述
Alice和Bob玩了一个古老的游戏:首先画一个 n × n n \times n n×n的点阵(下图 n = 3 n = 3 n=3) 接着,他们两个轮流在相邻的点之间画上红边和蓝边:
直到围成一个封闭的圈(面积不必为 1 1 1)为止,“封圈”的那个人就是赢家。因为棋盘实在是太大了 ( n ≤ 200 ) (n\le 200) (n≤200),他们的游戏实在是太长了!他们甚至在游戏中都不知道谁赢得了游戏。于是请你写一个程序,帮助他们计算他们是否结束了游戏?
输入
输入数据第一行为两个整数 n n n和 m m m。 m m m表示一共画了m条线。以后m行,每行首先有两个数字(x, y),代表了画线的起点坐标,接着用空格隔开一个字符,假如字符是" D D D ",则是向下连一条边,如果是" R R R "就是向右连一条边。输入数据不会有重复的边且保证正确。
输出
输出一行:在第几步的时候结束。假如 m m m步之后也没有结束,则输出一行“ d r a w draw draw”。
样例输入
3 5
1 1 D
1 1 R
1 2 D
2 1 R
2 2 D
样例输出
4
代码
代码自行理解,因为下午被人机惨了洛谷,我自由发言权没了,大概是被xxsJC了
,写法有很多种
数组开小点,可能会爆,不要加ios,也会爆。
#include<bits/stdc++.h>
using namespace std;
const int N=40000*2+10;
int id[210][210];
int fu[N];
int n,m,k;
int get(int x,int y) {
return id[x][y];
}
int fi(int x) {
if(x!=fu[x]) return fu[x]=fi(fu[x]);
return fu[x];
}
int main() {
// ios::sync_with_stdio(false);
// cin.tie();
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
for(int j=1; j<=n; j++) {
id[i][j]=k++;//
}
}
bool f=0;
for(int i=1; i<=(n+1)*(n+1); i++) fu[i]=i;
for(int i=1; i<=m; i++) {
int st,en,x,y;
char c;
cin>>x>>y>>c;w
st=get(x,y);
if(c=='D')en=get(x+1,y);
else en=get(x,y+1);
int fst=fi(st),fen=fi(en);
if(fst==fen) {
cout<<i<<"\n";
f=1;
break;
}
fu[fst]=fen;
}
if(!f) cout<<"draw\n";
}
银河英雄传说
题目背景
公元 5801 5801 5801 年,地球居民迁至金牛座 α \alpha α 第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。
宇宙历 799 799 799 年,银河系的两大军事集团在巴米利恩星域爆发战争。泰山压顶集团派宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集团点名将杨威利组织麾下三万艘战舰迎敌。
题目描述
杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在这次决战中,他将巴米利恩星域战场划分成
30000
30000
30000 列,每列依次编号为
1
,
2
,
…
,
30000
1, 2,\ldots ,30000
1,2,…,30000。之后,他把自己的战舰也依次编号为
1
,
2
,
…
,
30000
1, 2, \ldots , 30000
1,2,…,30000,让第
i
i
i 号战舰处于第
i
i
i 列,形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为 M i j
,含义为第
i
i
i 号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第
j
j
j 号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。
然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通过庞大的情报网络随时监听杨威利的舰队调动指令。
在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战舰分布情况,也会发出一些询问指令:C i j
。该指令意思是,询问电脑,杨威利的第
i
i
i 号战舰与第
j
j
j 号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。
作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以及回答莱因哈特的询问。
输入格式
第一行有一个整数 T T T( 1 ≤ T ≤ 5 × 1 0 5 1 \le T \le 5 \times 10^5 1≤T≤5×105),表示总共有 T T T 条指令。
以下有 T T T 行,每行有一条指令。指令有两种格式:
-
M i j
: i i i 和 j j j 是两个整数( 1 ≤ i , j ≤ 30000 1 \le i,j \le 30000 1≤i,j≤30000),表示指令涉及的战舰编号。该指令是莱因哈特窃听到的杨威利发布的舰队调动指令,并且保证第 i i i 号战舰与第 j j j 号战舰不在同一列。 -
C i j
: i i i 和 j j j 是两个整数( 1 ≤ i , j ≤ 30000 1 \le i,j \le 30000 1≤i,j≤30000),表示指令涉及的战舰编号。该指令是莱因哈特发布的询问指令。
输出格式
依次对输入的每一条指令进行分析和处理:
- 如果是杨威利发布的舰队调动指令,则表示舰队排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息。
- 如果是莱因哈特发布的询问指令,你的程序要输出一行,仅包含一个整数,表示在同一列上,第 i i i 号战舰与第 j j j 号战舰之间布置的战舰数目。如果第 i i i 号战舰与第 j j j 号战舰当前不在同一列上,则输出 − 1 -1 −1。
样例
样例输入
4
M 2 3
C 1 2
M 2 4
C 4 2
样例输出
-1
1
提示
战舰位置图:表格中阿拉伯数字表示战舰编号
思路
首先康题M i j
是进行合并操作,对于合并操作如果不对其进行路径压缩,时间可能会爆掉–TLE,而且对于我们的光明OJ,它的数据后6个点贼恶心,用路径压缩后,还卡常,必须用
i
o
s
ios
ios优化输入输出,但是
路径压缩之后,无法轻松的将每个队列的元素顺序记录下来,很难进行C i j
查询,所以要开两个数组:
一个储存队列的元素个数,另一个储存自身到父节点的距离,在每次合并时更新这两个数组。
AC Code(光明OJ)
#include<bits/stdc++.h>
using namespace std;
#define N 300001
int a[N],size[N],jul[N],n;
char b;
int x,y;
int xz(int x) {
if(a[x]!=x) {
int r=xz(a[x]);
jul[x]+=jul[a[x]];
a[x]=r;
}
return a[x];
}
int main() {
freopen("galaxy.in","r",stdin);
freopen("galaxy.out","w",stdout);
for(int i=1; i<=N; i++) {
a[i]=i;
size[i]=1;
}
scanf("%d",&n);
while(n--) {
ios::sync_with_stdio(false);
cin>>b>>x>>y;
int xx=xz(x),yy=xz(y);
if(b=='M') {
a[xx]=yy;
jul[xx]=size[yy];
size[yy]+=size[xx];
} else {
if(xx!=yy) printf("-1\n");
else printf("%d\n",abs(jul[x]-jul[y])-1);//这里-1是因为要不算尾部
}
}
fclose(stdin);
fclose(stdout);
return 0;
}
luogu AC Code
#include<bits/stdc++.h>
using namespace std;
#define N 100001
int a[N],size[N],jul[N]={},n;
char b;
int x,y;
int xz(int x) {
if(a[x]!=x){
int r=xz(a[x]);
jul[x]+=jul[a[x]];
a[x]=r;
}
return a[x];
}
void join(int x,int y){
x=xz(x);
y=xz(y);
a[x]=y;
jul[x]=size[y];
size[y]+=size[x];
}
int main(){
for(int i=1;i<=N;i++){
a[i]=i;
size[i]=1;
}
cin>>n;
for(int i=1;i<=n;i++){
cin>>b>>x>>y;
if(b=='M'){
join(x,y);
}else{
if(xz(x)!=xz(y)) cout<<"-1\n";
else cout<<abs(jul[x]-jul[y])-1<<"\n";//这里-1是因为要不算尾部
}
}
return 0;
}
两个代码最高数据点好像相差 100 m s 100ms 100ms,推荐用第一个代码但要去掉 f r e o p e n freopen freopen a n d and and f c l o s e fclose fclose