E. Melody
思路
将所有出现过的音量和音高看作一个点,一个声音看作一条边,连接起来。那么很容易知道要找的就是图上的一条欧拉路径(类似一笔画问题)
又已知存在欧拉路径的充要条件为:度数为奇数的点的个数为0或者2个,可以先判断部分为NO的情况。
数据范围要求离散化存点,然后用Hierholzer算法找欧拉路径。算法思想很简单,dfs时把走过的边都删掉,如果一个点递归处理完毕就加入到队列中(这里是存边,也是同理,不过是递归后记录边的序号),最后队列里记录的是反向的路径顺序。
dfs的起点是度数为奇数的点(如果存在),这里利用的是当前弧优化+vis数组来处理删边,以及比较最后n与ans的大小来判断图是否联通。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define endl '\n'
#define int long long
#define pb push_back
#define pii pair<int, int>
#define FU(i, a, b) for (int i = (a); i <= (b); ++i)
#define FD(i, a, b) for (int i = (a); i >= (b); --i)
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int maxn = 5e5 + 5, MAXN = maxn;
int ne[maxn]; // 当前弧优化
vector<pii> G[maxn];
vector<int> ans;
bool vis[maxn];
void dfs(int x) {
for (int i = ne[x]; i < G[x].size(); i = ne[x]) {
ne[x] = i + 1;
auto [y, eid] = G[x][i];
if (vis[eid])
continue;
vis[eid] = true;
dfs(y);
ans.push_back(eid); // 递归后记录边
}
}
void solve() {
ans.clear();
int n;
cin >> n;
FU(i, 1, 2 * n) {
G[i].clear();
ne[i] = 0;
vis[i] = 0;
}
map<int, int> mu, mv;
int im = 0;
FU(i, 1, n) {
int u, v;
cin >> u >> v;
if (mu[u] == 0)
mu[u] = ++im;
if (mv[v] == 0)
mv[v] = ++im;
G[mu[u]].pb({mv[v], i}); // u -> {v,eid}
G[mv[v]].pb({mu[u], i});
}
int cntj = 0;
int qd = 1;
FU(i, 1, im) {
if (G[i].size() % 2 == 1)
qd = i, cntj++;
}
if (cntj != 0 && cntj != 2) {
cout << "NO\n";
return;
}
dfs(qd);
if (ans.size() != n) { // 说明不连通
cout << "NO\n";
return;
}
cout << "Yes\n";
for (int e : ans) {
cout << e << " ";
}
cout << endl;
}
signed main() {
#ifdef ONLINE_JUDGE
#else
freopen("../in.txt", "r", stdin);
#endif
cin.tie(0)->ios::sync_with_stdio(0);
int T = 1;
cin >> T;
while (T--) {
solve();
}
return 0;
}