题解 P9601 【[IOI2023] 最长路程】
注意到使用
直接当成一般图来做是 NPC 问题,考虑利用题目给出的性质,可以发现:
- 这张图至多存在两个连通块。
证明:如果存在三个连通块,分别抓出三个点
- 如果存在两个连通块,则两个连通块均为团。
证明:如果某个连通块中存在两个无边的
因此,若存在两个连通块,则我们以任意形式遍历其中较大者的所有点即可。
否则,考虑利用形如“若
考虑依次加入每个点。注意到无论何时原图都满足其至多存在两个连通块,考虑维护两条路径
接下来考虑加入点
- 若
P 的末端与i 间有边,则将i 加入P 的末尾。 - 若
Q 的末端与i 间有边,则将i 加入Q 的末尾。 - 否则,注意到此时
P, Q 的末端直接一定有边,则将P, Q 串起来作为新P ,将i 单独作为新Q 即可。
若
否则,
现在考虑减少询问次数。
注意到上面构造
注意到最终限制
最终我们发现按照之前的询问方式可能会恰好多出一次询问。注意到当
最终,
代码:
#include <iostream>
#include <vector>
#include "longesttrip.h"
using namespace std;
pair<int, int> find1(int u, vector<int> v1){
if (v1.size() == 1) return make_pair(u, v1[0]);
int mid = v1.size() / 2;
vector<int> v2;
for (int i = 1; i <= mid; i++){
v2.push_back(v1.back());
v1.pop_back();
}
if (are_connected(vector<int>{u}, v1)) return find1(u, v1);
return find1(u, v2);
}
pair<int, int> find2(vector<int> v1, vector<int> v2){
if (v1.size() == 1) return find1(v1[0], v2);
int mid = v1.size() / 2;
vector<int> v3;
for (int i = 1; i <= mid; i++){
v3.push_back(v1.back());
v1.pop_back();
}
if (are_connected(v1, v2)) return find2(v1, v2);
return find2(v3, v2);
}
vector<int> longest_trip(int N, int D){
vector<int> v1, v2;
v1.push_back(0);
v2.push_back(1);
for (int i = 2; i + 1 < N; i += 2){
if (are_connected(vector<int>{v1.back()}, vector<int>{i})){
v1.push_back(i);
if (are_connected(vector<int>{i}, vector<int>{i + 1})){
v1.push_back(i + 1);
} else if (are_connected(vector<int>{v2.back()}, vector<int>{i + 1})){
v2.push_back(i + 1);
} else {
while (!v2.empty()){
v1.push_back(v2.back());
v2.pop_back();
}
v2.push_back(i + 1);
}
} else if (are_connected(vector<int>{v2.back()}, vector<int>{i})){
v2.push_back(i);
if (are_connected(vector<int>{i}, vector<int>{i + 1})){
v2.push_back(i + 1);
} else {
v1.push_back(i + 1);
}
} else {
if (are_connected(vector<int>{i}, vector<int>{i + 1})){
while (!v2.empty()){
v1.push_back(v2.back());
v2.pop_back();
}
v2.push_back(i);
v2.push_back(i + 1);
} else {
v1.push_back(i + 1);
while (!v2.empty()){
v1.push_back(v2.back());
v2.pop_back();
}
v2.push_back(i);
}
}
}
if (N % 2 == 1){
if (are_connected(vector<int>{v1.back()}, vector<int>{N - 1})){
v1.push_back(N - 1);
} else if (are_connected(vector<int>{v2.back()}, vector<int>{N - 1})){
v2.push_back(N - 1);
} else {
while (!v2.empty()){
v1.push_back(v2.back());
v2.pop_back();
}
v2.push_back(N - 1);
}
}
if (!are_connected(v1, v2)){
if (v1.size() > v2.size()) return v1;
return v2;
}
int p = v1[0], q = v2[0], size1 = v1.size(), size2 = v2.size();
vector<int> ans;
if (are_connected(vector<int>{p}, vector<int>{q})){
for (register int i = size1 - 1; i >= 0; i--){
ans.push_back(v1[i]);
}
for (register int i = 0; i < size2; i++){
ans.push_back(v2[i]);
}
} else {
int r = v1[size1 - 1], s = v2[size2 - 1];
if (are_connected(vector<int>{r}, vector<int>{s})){
for (register int i = 0; i < size1; i++){
ans.push_back(v1[i]);
}
for (register int i = size2 - 1; i >= 0; i--){
ans.push_back(v2[i]);
}
} else if (are_connected(vector<int>{p}, vector<int>{s})){
for (register int i = size1 - 1; i >= 0; i--){
ans.push_back(v1[i]);
}
for (register int i = size2 - 1; i >= 0; i--){
ans.push_back(v2[i]);
}
} else {
int posu, posv;
pair<int, int> pr = find2(v1, v2);
for (register int i = 0; i < size1; i++){
if (v1[i] == pr.first){
posu = i;
break;
}
}
for (register int i = 0; i < size2; i++){
if (v2[i] == pr.second){
posv = i;
break;
}
}
for (register int i = posu - 1; i >= 0; i--){
ans.push_back(v1[i]);
}
for (register int i = size1 - 1; i >= posu; i--){
ans.push_back(v1[i]);
}
for (register int i = posv; i < size2; i++){
ans.push_back(v2[i]);
}
for (register int i = 0; i < posv; i++){
ans.push_back(v2[i]);
}
}
}
return ans;
}