一共有 n 个数,编号是 1∼n,最开始每个数各自在一个集合中。 现在要进行 m 个操作,操作共有两种: M a b,将编号为 a 和 b 的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作; Q a b,询问编号为 a 和 b 的两个数是否在同一个集合中; 输入格式 第一行输入整数 n 和 m。 接下来 m 行,每行包含一个操作指令,指令为 M a b 或 Q a b 中的一种。
输出格式 对于每个询问指令 Q a b,都要输出一个结果,如果 a 和 b 在同一集合内,则输出 Yes,否则输出 No。 每个结果占一行。
数据范围 1≤n,m≤10^5 输入样例: 45 M 12 M 34 Q 12 Q 13 Q 34 输出样例: Yes No Yes
给定一个包含 n 个点(编号为 1∼n)的无向图,初始时图中没有边。 现在要进行 m 个操作,操作共有三种: C a b,在点 a 和点 b 之间连一条边,a 和 b 可能相等; Q1 a b,询问点 a 和点 b 是否在同一个连通块中,a 和 b 可能相等; Q2 a,询问点 a 所在连通块中点的数量; 输入格式 第一行输入整数 n 和 m。 接下来 m 行,每行包含一个操作指令,指令为 C a b,Q1 a b 或 Q2 a 中的一种。
输出格式 对于每个询问指令 Q1 a b,如果 a 和 b 在同一个连通块中,则输出 Yes,否则输出 No。 对于每个询问指令 Q2 a,输出一个整数表示点 a 所在连通块中点的数量 每个结果占一行。
数据范围 1≤n,m≤10^5 输入样例: 55 C 12 Q1 12 Q2 1 C 25 Q2 5 输出样例: Yes 2 3
constint N = 1e5+5; int h[N],Size;// h[N]存储堆中的值,Size存放堆的元素个数 int n,m;
voiddown(int u){// n个节点的堆最多约logn层,递归消耗不会很大 int t = u;// t的元素值为当前节点与它的左右孩子三者的最小值,标记最小值 if (2*u <= Size && h[2*u] < h[t]) t = 2*u;// 左孩子存在且左孩子元素值小则更新t为左孩子 if (2*u+1 <= Size && h[2*u+1] < h[t]) t = 2*u+1;// 右孩子同理 if (u != t){// 如果当前节点的元素值不是三者最小值 swap(h[u],h[t]);// 交换u和t的元素值 down(t);// 重复down过程直到底层 } }
voidup(int u){// up操作只需要和父节点比较 while (u / 2 && h[u/2] > h[u]){// 父节点存在且当前节点元素值比父节点小 swap(h[u],h[u/2]); u /= 2;// 重复过程直到堆顶 } }
intmain(){ scanf("%d%d",&n,&m); for (int i = 1;i <= n;i ++) scanf("%d",&h[i]); Size = n; for (int i = n >> 1;i ; i--) down(i);// O(n)建堆 while (m--){ printf("%d ",h[1]);// 输出堆顶(最小值) h[1] = h[Size];// 删除堆顶(用最后一个数覆盖第一个数) 并且长度减1 Size --;// 可以合并为h[1] = h[Size--] down(1);// 让覆盖好的数往下面走 } return0; }
拓展,循环版本的down函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
voiddown(int u) { int t = u; while (1) { if (2 * u <= cnt && h[2 * u] < h[t]) t = 2 * u; if (2 * u + 1 <= cnt && h[2 * u + 1] < h[t]) t = 2 * u + 1; if (u != t) { swap(h[u], h[t]); u = t; } elsebreak; } }