0%

Codeforces Round 1026 (Div. 2)-CF-1026-E. Melody

题目大意

https://codeforces.com/contest/2110/problem/E

题目描述
给定 nn 个不同的声音,每个声音由音量 viv_i 和音高 pip_i 两个属性组成。
你需要将这 nn 个声音排列成一个序列,每个声音恰好出现一次。
一个合法的序列需要同时满足以下两个条件:

  1. 任意两个相邻的声音,必须且仅有一个属性相同(即要么音量相同且音高不同,要么音高相同且音量不同)。

  2. 任意连续的三个声音,它们的音量不能完全相同,音高也不能完全相同(即不能出现连续三个声音音量一样,也不能出现连续三个声音音高一样)。

请判断是否存在这样的合法序列。如果存在,输出 YES 以及排列后各个声音的初始编号;如果不存在,输出 NO

输入格式
第一行包含一个整数 tt1t1041 \le t \le 10^4),表示测试用例的数量。
每个测试用例的第一行包含一个整数 nn1n21051 \le n \le 2 \cdot 10^5),表示声音的数量。
接下来的 nn 行,每行包含两个整数 viv_ipip_i1vi,pi1091 \le v_i, p_i \le 10^9),分别表示第 ii 个声音的音量和音高。保证同一个测试用例中不会出现音量和音高都完全相同的两个声音。
所有测试用例中 nn 的总和不超过 21052 \cdot 10^5

输出格式
如果存在合法的序列,第一行输出 YES,第二行输出 nn 个整数,表示合法的声音排列顺序对应的原始编号(从 1 开始)。
如果不存在,输出 NOYESNO 忽略大小写。

样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
输入:
5
4
179 239
179 179
239 179
239 239
3
1 1
2 1
3 1
1
5 7
5
1 1
1 2
2 1
2 2
99 99
7
1 1
1 3
2 1
2 2
3 1
3 2
3 3

输出:
YES
4 3 2 1
NO
YES
1
NO
YES
3 4 6 7 2 1 5

样例解释
在第一个测试用例中,按照 4, 3, 2, 1 的顺序排列,声音序列为:(239,239)(239,179)(179,179)(179,239)(239,239)-(239,179)-(179,179)-(179,239)。包含了所有的声音,并且任意两个相邻的声音都仅有一个属性(音量或音高)不同,满足所有条件。
在第二个测试用例中,3 个声音的音高全部是 1,这意味着无论如何排列,都会出现连续三个声音音高相同的情况,因此无法构造合法的序列。

思路讲解

Codeforces Round 1081 (Div. 2)-CF-1081-E. Swap to Rearrange(按值进行图论建模)(欧拉回路)

注意,欧拉路径存在的充要条件(在无向连通图上),就是奇数度数节点数量等于0 或者 2。说白了,就是相比于回路,我们允许你的这个起点和终点搞点怪,只进不出或者只出不进,但是我们不允许你的其他点违反这一法则。

这两道题目应该是比较类似的。

在算法竞赛中,采用排除法还是一个比较好的办法。这道题目其实还是一样的,其他方法不太适合解决这种问题,那么只能够求助于图论建模了。

我们想问怎么样建这张图,实际上这个题目是rearrangement问题,它只有一个可以连边的东西,对吧,那就是V和P之间去连,V和P之间按值连接。这也是使用排除法,因为其他东西你都不知道,它怎么排,你怎么去连,对吧。

image

然后我们还是一样的套路,就是一开始是一张无向图,然后通过遍历进行定向。然后我们怎么样进行这个呢?这个遍历需要遵循怎样的规则呢?我个人认为呢,这个遍历所需要遵循的规则就是,首先它不能够去访问父节点(会造成连续的两个声音音量和音高重合),然后每条边仅遍历一次。

DFS写的话,这个递归它不符合这个线性特征,所以说的话不太适合。我们决定采用类似于CF 1081e的做法,但有一个问题,CF 1081e可以确保所有点的入度和出度。如果这张图是一张无像图,认为所有点的度数都是偶数,可以保证这一点,因为不保证这一点的图就是错的,输出no就可以了。但这道题不一定,如果作无像图,每个点的度数不一定是偶数,只是偶数时才有解,它可以不是偶数这也是可以的。

不过我们上面也说了,通过对欧拉路径的学习,我们知道:

奇数度数节点数量等于 0 或者 2

注意还有判断图是否联通,这个也是非常重要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
if (odd_cnt!=0 && odd_cnt!=2) {
cout<<"NO\n";
return;
}

vector<ll> ans_ls;
auto dfs=[&](auto && self,ll u) -> void {
while (!g[u].empty()) {
auto it=g[u].begin();
auto [to,id]=*it;
g[u].erase(it);
g[to].erase({u,id});
self(self,to);
ans_ls.push_back(id);
}
};
dfs(dfs,max(start_node,g.begin()->first));
// 判断图是否联通,如果不联通,也是错的。
if (SZ(ans_ls)!=N) {
cout<<"NO\n";
return;
}
reverse(all(ans_ls));
cout<<"YES\n";
for (auto u:ans_ls) {
cout<<u<<" ";
}
cout<<"\n";

为什么 Euler 路径红色这样子写有问题呢?其实也很容易理解,因为我们要把 ans_ls 反过来的呀!所以说,肯定需要先遍历完后续的点,才能塞入这条边啊!其实简单来说,就是要和正常的反过来

image

还有我们要注意起点选择,就是DFS的起点是非常重要的。在欧拉路径当中,欧拉回路我们当然是无所谓,但是欧拉路径的这个起始位置是要注意的。

我们采用的策略是有奇数点就选奇数点,没有奇数点就选随便选一个点。

image

AC代码

AC
https://codeforces.com/contest/2110/submission/364850915

https://codeforces.com/contest/2110/submission/321091518

心路历程(WA,TLE,MLE……)

为什么 Euler 路径红色这样子写有问题呢?其实也很容易理解,因为我们要把 ans_ls 反过来的呀!所以说,肯定需要先遍历完后续的点,才能塞入这条边啊!其实简单来说,就是要和正常的反过来

image

还有我们要注意起点选择,就是DFS的起点是非常重要的。在欧拉路径当中,欧拉回路我们当然是无所谓,但是欧拉路径的这个起始位置是要注意的。

我们采用的策略是有奇数点就选奇数点,没有奇数点就选随便选一个点。

image