题解:P12649 [KOI 2024 Round 2] 收集彩球
题目分析
首先注意到,可以将颜色看成结点,盒子中上方球的颜色和下方球的颜色之间连边,这样就可以将盒子中的球上下关系转化为一张图。图中有若干连通块,每个块内的颜色球移动不影响块外的球答案(因为块内的颜色和块外不需要也不能进行移动)。所以考虑分别处理每个连通块。下文中,符合要求是指每种颜色的两个球都在同一个盒子中。
可以发现,想要让块内颜色球位置符合要求,需要先将某两种颜色球移动到空盒(
现在考虑验证某个块通过移动符合要求可行性的方法。注意到只有
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, a[N][5];
int fa[N];
int getfa(int x) {return (x == fa[x]) ? x : (fa[x] = getfa(fa[x]));}
void merge(int x, int y) {
x = getfa(x), y = getfa(y);
if (x != y) fa[x] = y;
}
int cnt[N];
vector<int> g[N];
int up[N], pos[N][5];
bool nok(int x) { // 判断连通块是否不符合要求
int flag = 0;
for (int i = 0; i < g[x].size(); i++) {
if (up[g[x][i]] == 2) {
if (flag) return true;
flag = g[x][i];
}
}
return false;
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= n; i++) {
cin >> a[i][1] >> a[i][2];
merge(a[i][1], a[i][2]);
up[a[i][1]]++;
pos[a[i][1]][1 + (pos[a[i][1]][1] != 0)] = i;
}
for (int i = 1; i <= n; i++) {
int tmp = getfa(i);
cnt[tmp]++;
g[tmp].push_back(i);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
if (cnt[i] == 0 || cnt[i] == 1) continue;
if (nok(i)) {
cout << -1 << '\n';
return 0;
}
ans += cnt[i] + 1;
}
cout << ans << '\n';
return 0;
}