E - Easy Compare-and-Set
題意
給定n個條件,如果存在一個合法序列使得這n個判斷條件成立,則輸出Yes和這個合法序列,否則輸出No。
分析
首先可以發現對於\(w_i = 0\)的操作我們可以在處理完\(w_i = 1\)的操作之後討論一下即可。
發現\(a_i\)和\(b_i\)很大需要對其進行離散化操作。離散化後可以將\(a_i\)和\(b_i\)看成點,即一條從a走向b的有向邊。將所有的\(w_i = 1\)的邊連線起來之後,則可以發現題目等價於問以c為起點能否恰好經過每條邊一次,最後跑一邊尤拉回路記錄路徑即可。
對於\(w_i = 0\)的情況我們可以先將所有\(a_i \neq c\)的操作先用掉,對於\(a_i = c\)的操作在跑圖時插在第2的點中間即可。
最後注意判斷無解情況,具體看程式碼實現。
程式碼實現
#include <bits/stdc++.h>
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, c;
std::cin >> n >> c;
std::unordered_map<int, int> mp;
std::vector<int> w(n);
std::vector<std::array<int, 3>> edges(n);
int cur = 0;
mp[c] = cur++;
c = mp[c];
for (int i = 0; i < n; ++i) {
int a, b;
std::cin >> a >> b >> w[i];
if (!mp.count(a)) mp[a] = cur++;
if (!mp.count(b)) mp[b] = cur++;
a = mp[a], b = mp[b];
edges[i] = {a, b, w[i]};
}
std::vector<int> din(cur), dout(cur);
std::vector<std::vector<std::pair<int, int>>> g(cur);
std::set<int> dot;
for (int i = 0; i < n; ++i) {
auto [a, b, e] = edges[i];
if (e == 1) {
g[a].emplace_back(b, i);
dot.emplace(a), dot.emplace(b);
din[b] += 1, dout[a] += 1;
}
}
for (int num = 0; auto x : dot) if (din[x] != dout[x]) {
if (din[x] < dout[x]) {
if (x != c || dout[x] - din[x] > 1) {
std::cout << "No" << '\n';
return 0;
}
} else {
num += 1;
if (num > 1 || din[x] - dout[x] > 1) {
std::cout << "No" << '\n';
return 0;
}
}
}
std::vector<bool> use(n);
std::vector<int> path, ans1, ans2;
for (int i = 0; i < n; ++i) {
auto [a, b, _] = edges[i];
if (w[i] == 0 && a != c) {
use[i] = true;
ans1.emplace_back(i);
} else if (w[i] == 0) {
use[i] = true;
ans2.emplace_back(i);
}
}
auto dfs = [&](auto &&self, int u) ->void {
while(size(g[u])) {
auto [v, e] = g[u].rbegin()[0];
use[e] = true;
g[u].pop_back();
self(self, v);
path.emplace_back(e);
}
};
dfs(dfs, c);
if (((int)size(path) == 0 && size(ans2)) || std::count(use.begin(), use.end(), true) != n || (cur == 1 && std::count(w.begin(), w.end(), 1) != n)) {
std::cout << "No" << '\n';
} else {
std::cout << "Yes" << '\n';
for (int i = 0; i < (int)size(ans1); ++i) {
std::cout << ans1[i] + 1 << " ";
}
int pos = size(path);
for (int i = 0; i < (int)size(path); ++i) {
if (edges[path.rbegin()[i]][0] == c) {
std::cout << path.rbegin()[i] + 1 << " \n"[i == (int)size(path) - 1];
} else {
pos = i;
break;
}
}
for (int i = 0; i < (int)size(ans2); ++i) {
std::cout << ans2[i] + 1 << " ";
}
for (int i = pos; i < (int)size(path); ++i) {
std::cout << path.rbegin()[i] + 1 << " \n"[i == (int)size(path) - 1];
}
}
}
最後感謝學弟提供的hack樣例,不然實在是沒想到為什麼wa6。
╲(。◕‿◕。)╱