D. Colored Portals
思路:
还是很好的题。
枚举,找规律,发现总共6种颜色,对于一种颜色只有唯一的另一种颜色不能直接到达(BG -RY BR-GY BY-GR),而只需要找到任意一个不是这两种颜色的城市(即只有一个共同颜色的剩余四种城市),就可以作为中转。后面就很简单了。
如果x,y可以直接到达,那么最短距离就是|x-y|
否则在只有一个共同颜色的城市中找最近的中转点:
1.如果[x,y]内有中转点,那么最短距离依旧是|x-y|
2.否则在[0,x]和[y,n]中二分查找最接近x、y的点作为中转点
简化一下这个过程,直接找到大于等于x的最小点和小于x的最大点作为中转点即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 0x3f3f3f3f;
unordered_map<string, int> CI;
vector<int> c[6]; //升序维护6种颜色城市编号
vector<int> ys; //维护每种城市的颜色
void init() {
CI["BG"] = 0;
CI["BR"] = 1;
CI["BY"] = 2;
CI["GR"] = 3;
CI["GY"] = 4;
CI["RY"] = 5;
}
const int cn[6] = {5, 4, 3, 2, 1, 0}; //不能直接到达的颜色
const int co[6][4] = {{1, 2, 3, 4}, {0, 2, 3, 5}, {0, 1, 4, 5}, {0, 1, 4, 5}, {0, 2, 3, 5}, {1, 2, 3, 4}}; //有一个相同颜色
void solve() {
int n, q;
cin >> n >> q;
ys.clear();
ys.resize(n + 1);
for (int i = 0; i < 6; ++i) c[i].clear();
for (int i = 1; i <= n; ++i) {
string c1;
cin >> c1;
ys[i] = CI[c1];
c[CI[c1]].push_back(i);
}
while (q--) {
int i, j;
cin >> i >> j;
if (ys[i] != cn[ys[j]])
cout << abs(i - j) << endl;
else {
// 二分搜索,查找最靠近i和j的颜色与i,j都不同的节点k
int minDist = MAXN;
for (int color : co[ys[i]]) {
// 查找大于等于i的最小点(it)作为中转点
auto it = lower_bound(c[color].begin(), c[color].end(), i);
if (it != c[color].end()) {
int k2 = *it;
minDist = min(minDist, abs(i - k2) + abs(j - k2));
}
// 小于i的最大点(it的前一个点)作为中转点
if (it != c[color].begin()) {
int k1 = *prev(it);
minDist = min(minDist, abs(i - k1) + abs(j - k1));
}
}
cout << (minDist >= MAXN ? -1 : minDist) << endl;
}
}
}
int main() {
cin.tie(0)->ios::sync_with_stdio(0);
init();
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}