连通块中点的数量
问题描述
给定一个初始包含 nn 个点,00 条边的图。第 ii 个点的编号为 ii。(1≤i≤n)(1≤i≤n)
现在要进行 mm 次操作,操作共三种:
C a b:在点 aa 与点 bb 之间连一条边,a,ba,b 可能相等。Q1 a b:询问点 aa 与点 bb 是否在一个连通块中,a,ba,b 可能相等。Q2 a:询问点 aa 所在连通块中点的数量。
输入格式
第一行输入两个正整数 n,mn,m。(1≤n,m≤105)(1≤n,m≤105)
接下来 mm 行,每行输入包含一种操作。(1≤a,b≤n)(1≤a,b≤n)
输出格式
对于查询 Q1 a b:若 a,ba,b 在一个连通块中,输出 Yes,否则输出 No。
对于查询 Q2 a:输出点 aa 所在连通块中点的数量。
样例输入
5 7
C 1 2
Q1 1 2
Q2 1
C 2 5
Q2 5
Q1 1 1
Q1 2 3
样例输出
Yes
2
3
Yes
No
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;
public class Main {
static int N=100010,mod=998244353;
static int size[]=new int[N];
static int p[]=new int[N];
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
int n=Integer.parseInt(st.nextToken()),m=Integer.parseInt(st.nextToken());
for (int i = 1; i <= n; i++) {
p[i]=i;
size[i]=1;
}
for (int i = 0; i < m; i++) {
st=new StringTokenizer(br.readLine());
// C a b:在点 a 与点 b 之间连一条边,(a,b) 可能相等。
// Q1 a b:询问点 a 与点 b 是否在一个连通块中,(a,b) 可能相等。
// Q2 a:询问点 a 所在连通块中点的数量。
String ope=st.nextToken();
int a=Integer.parseInt(st.nextToken());
if(st.hasMoreTokens()){
int b=Integer.parseInt(st.nextToken());
if("C".equals(ope)){
if(a==b)continue;
union(a,b);
}else{
if(a==b){
bw.write("Yes\n");
}else{
if(find(a)==find(b)){
bw.write("Yes\n");
}else{
bw.write("No\n");
}
}
}
}else{
int fa=find(a);
bw.write(size[fa]+"\n");
}
}
br.close();
bw.flush();
bw.close();
}
static void union(int a,int b){
a=find(a);b=find(b);
if(a!=b){
p[a]=b;
size[b]+=size[a];
}
}
static int find(int i){
if(i!=p[i]){
p[i]=find(p[i]);
}
return p[i];
}
}
堆箱子(4星)
问题描述
暑假到了,小蓝打完蓝桥杯去工厂兼职,成为了工厂里的搬运工,工厂里面初始有 nn 个箱子平铺在地面上,每个箱子都有一个编号,分别是 1,2,3...n1,2,3...n。由于小蓝喜欢算法竞赛,他想到了一个问题。
现在有 22 种操作,具体操作如下:
1 a b,11 号操作,将 aa 位置所在的所有箱子搬到 bb 位置所在的箱子上面,已经在相同位置的箱子不能进行此操作。2 a,22 号操作,查询 aa 位置下有多少个箱子。
现在小蓝有 qq 次操作,对于每次操作 22 ,你需要输出结果。
输入格式
第一行二个整数 n,qn,q ,表示箱子的数量和操作次数。
接下来 qq 行,每行输入代表一个具体操作。
输出格式
对所有的操作 22 ,输出其结果。
样例输入
6 6
1 4 2
2 2
2 4
1 2 3
2 2
2 4
样例输出
0
1
1
2

在第一次操作后,44 下面是 22 号箱子。
在第二次操作后,44 下面是 22 号箱子,22 下面是 33 号箱子。
评测数据规模
1≤n≤3×104,1≤q≤1051≤n≤3×104,1≤q≤105。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.*;
public class Main {
static int N=3*10010;
static int size[]=new int[N];//该根节点对应树有多少节点 最下面的节点是根节点
static int p[]=new int[N];//父节点
static int d[]=new int[N];//该位置下面有多少个箱子 也就是答案
static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
StringTokenizer st=new StringTokenizer(br.readLine());
int n=Integer.parseInt(st.nextToken()),q=Integer.parseInt(st.nextToken());
for (int i = 1; i <= n; i++) {
p[i]=i;
size[i]=1;
}
for (int i = 0; i < q; i++) {
st=new StringTokenizer(br.readLine());
int ope=Integer.parseInt(st.nextToken());
if(ope==1){
int a=Integer.parseInt(st.nextToken()),b=Integer.parseInt(st.nextToken());
//a位置所在的所有箱子搬到b 位置所在的箱子上面
//我们只更新a原来的父节点pa的d[fa] 其子节点先不用跟新 我们后续只需要 d[i]=d[i]+d[p[i]];
//当然 p[i]也可能没有被更新过 所有我们要在find里更新
if(a==b)continue;
union(a,b);
}else{
int a=Integer.parseInt(st.nextToken());
find(a);
bw.write(d[a]+"\n");
}
}
br.close();
bw.flush();
bw.close();
}
static void union(int a,int b){
a=find(a);b=find(b);
if(a!=b){
d[a]=size[b];
p[a]=b;
size[b]+=size[a];
}
}
static int find(int i){
if(i!=p[i]){
int v=find(p[i]);
d[i]=d[i]+d[p[i]];
p[i]=v;
}
return p[i];
}
}