AtCoder Beginner Contest 372 E Solution
题意
求连通块内第 \(k\) 大节点标号,\(1\le k\le {\color{red}{10}}\)。
思路
并查集+vector
维护前 \(k\) 大标号。
首先给每个节点 \(i\) 的连通块中的 vector
push 一个 \(i\)。
接下来,在并查集合并函数中,可以使用一个简单的方法直接实现 \(O(n+m)\) 有序数列归并,那就是调用 STL merge
函数。需要传入两个数组的前闭后开指针以及一个存储结果数组的起始指针。注意,若使用 vector
作为存储结果的数组,需要提前在该数组中申请 \((n+m)\) 个元素的空间,否则 RE。
查询时直接输出即可。
详情见代码。
代码
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <vector>
using namespace std;
const int N = 2e5 + 10;
int n, m, f[N];
vector<int> vec[N];
inline int find(int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
inline void mrge(int x, int y)
{
x = find(x), y = find(y);
if (x == y)
return;
if (vec[x].size() < vec[y].size())
swap(x, y);
f[y] = x;
vector<int> tmp(vec[x].size() + vec[y].size());
merge(vec[x].begin(), vec[x].end(), vec[y].begin(), vec[y].end(), tmp.begin());
// assert(vec[x].size() and vec[y].size());
// fprintf(stderr, "%llu\n", tmp.size());
// printf("%d %d\n", x, y);
reverse(tmp.begin(), tmp.end());
tmp.resize(min<int>(tmp.size(), 10));
reverse(tmp.begin(), tmp.end());
vec[x] = tmp;
vec[y].clear();
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
{
vec[i].emplace_back(i);
f[i] = i;
}
for (int i = 1, x, y, z; i <= m; i++)
{
// fprintf(stderr, "%d\n", i);
scanf("%d%d%d", &x, &y, &z);
if (x == 1)
{
mrge(y, z);
continue;
}
y = find(y);
if (vec[y].size() < z)
{
puts("-1");
continue;
}
// for (auto &j : vec[y])
// printf("%d ", j);
// putchar('\n');
printf("%d\n", vec[y][vec[y].size() - z]);
}
}