Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Think of each employee as a vertex. Two employees are immediately connected if they share at least one language. After that, indirect correspondence is just reachability in this graph.
Inside one connected component, everybody can already communicate via translators, so only the number of connected components matters. Ask yourself: how much can one paid lesson change that number?
If you teach employee some language , then 's whole component can merge with the component that already knows . A single lesson cannot merge with two different components at once, because if were present in two components, those components would already be the same one.
So if the initial employee graph has connected components and at least one language is known somewhere, you need at least lessons, and you can achieve exactly by picking one component as a hub and connecting every other component to it with one lesson.
Implement the components with DSU: for each language, remember the first employee who knows it; every later employee with that language gets unioned with the first. Let be the number of DSU roots. If , the answer is (the first lesson connects nobody, nasty little edge case). Otherwise the answer is .
Build a graph on employees.
Then two employees can correspond iff they are in the same connected component of this graph, because translators can relay the message along a path. So the whole problem is really about merging connected components. Cute disguise, but it is just components wearing a fake mustache.
Suppose employee learns language .
There are only two possibilities:
And that is all.
Why can one lesson merge with at most one other component? Because after we build the initial graph, every language belongs to exactly one connected component. If language were known in two different components, those components would already be connected through employees knowing . Contradiction.
So each lesson decreases the number of employee-components by at most .
Let:
Then the answer is
Lower bound: Each lesson reduces the number of connected components by at most . To go from components down to , we need at least
lessons.
Upper bound:
Pick any component that contains at least one known language. Use it as a hub.
For every other component, choose one employee in that component and teach them any language known in the hub. That single lesson merges the entire component into the hub.
That uses exactly one lesson per extra component, so total cost is
Lower bound matches upper bound, so done.
This is the only trap in the problem.
If nobody knows any language initially, then the first lesson cannot connect two employees; it only gives one person some language. So after the first lesson, the number of connected components is still .
From then on, each additional lesson can reduce the number of components by at most , so we need at least
lessons in total.
And is achievable: just teach the same language to all employees.
So the all-zero case is exactly , not . That off-by-one has killed many perfectly decent submissions. Brutal.
Use DSU on employees.
For each language , store the first employee who knows it.
When employee knows language :
first[\ell] = i;first[\ell].Why does this work? Because all employees knowing the same language must belong to the same connected component, and DSU handles transitivity for free.
Employees with languages simply stay isolated in the DSU, which is exactly what we want.
Therefore the formula above is correct.
Let
The DSU operations take
which is basically linear for any sane universe.
Memory usage is
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct DSU {
vector<int> p, sz;
DSU(int n) : p(n), sz(n, 1) {
iota(p.begin(), p.end(), 0);
}
int find(int x) {
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
bool unite(int a, int b) {
a = find(a);
b = find(b);
if (a == b) return false;
if (sz[a] < sz[b]) swap(a, b);
p[b] = a;
sz[a] += sz[b];
return true;
}
};
int main() { setIO();
int n, m;
cin >> n >> m;
DSU dsu(n);
vector<int> first(m + 1, -1);
int totalKnown = 0;
for (int i = 0; i < n; ++i) {
int k;
cin >> k;
totalKnown += k;
for (int j = 0; j < k; ++j) {
int lang;
cin >> lang;
if (first[lang] == -1) first[lang] = i;
else dsu.unite(i, first[lang]);
}
}
int comps = 0;
for (int i = 0; i < n; ++i) {
if (dsu.find(i) == i) ++comps;
}
if (totalKnown == 0) cout << n << '\n';
else cout << comps - 1 << '\n';
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
struct DSU {
vector<int> p, sz;
DSU(int n) : p(n), sz(n, 1) {
iota(p.begin(), p.end(), 0);
}
int find(int x) {
if (p[x] == x) return x;
return p[x] = find(p[x]);
}
bool unite(int a, int b) {
a = find(a);
b = find(b);
if (a == b) return false;
if (sz[a] < sz[b]) swap(a, b);
p[b] = a;
sz[a] += sz[b];
return true;
}
};
int main() { setIO();
int n, m;
cin >> n >> m;
DSU dsu(n);
vector<int> first(m + 1, -1);
int totalKnown = 0;
for (int i = 0; i < n; ++i) {
int k;
cin >> k;
totalKnown += k;
for (int j = 0; j < k; ++j) {
int lang;
cin >> lang;
if (first[lang] == -1) first[lang] = i;
else dsu.unite(i, first[lang]);
}
}
int comps = 0;
for (int i = 0; i < n; ++i) {
if (dsu.find(i) == i) ++comps;
}
if (totalKnown == 0) cout << n << '\n';
else cout << comps - 1 << '\n';
}