暴力算法
有一个比较显然的暴力算法:考虑奇数和偶数长度两种情况,用 BFS 进行同色扩展即可完成。但是复杂度较高,需要实质上 \(\Theta(n^2 + m^2)\) 的时间进行 BFS。
优化思路
这题的 nb 之处在于其优化思路。这道题的优化思路非常的自然。我们发现瓶颈在于 \(m^2\),那么我们是否能在不影响答案的情况下,删掉一堆边、删到 \(n\) 级别然后做同样的暴力算法呢?
当然是可以的。首先,我们忽略图中的异色边,只关注同色边。假设忽略这些边之后分成了若干个连通块。考虑连通块的特性,如果这个连通块是个二分图的话,那么不存在奇环。在这个题里面没有奇环是一个很重要的性质,因为如果你想在一个全是同色边的连通块里删边,你必须保证对应的偶环和奇环必须都存在,要不然会影响答案。
回到二分图上,如果是个二分图我们可以随便取一个生成树即可,因为二分图的生成树也是二分图嘛。
如果不是二分图,说明我们要造的新图必须奇环、偶环都要存在,所以我们可以加一个自环来进行调节即可。
再加上那些异色边,就可以得到一张性质和原来图相同的、边级别在 \(\Theta(n)\) 的图了。之后尽情暴力即可。
代码
// P5292.cpp #include <bits/stdc++.h> using namespace std; const int MAX_N = 5050, MAX_M = 1e6 + 200; typedef pair<int, int> pii; int n, m, q, col[MAX_N], head[MAX_N], current, ui[MAX_M], vi[MAX_M], mem[2][MAX_N]; bool tag[MAX_N], travel[MAX_N][MAX_N], already[MAX_N]; char str[MAX_N]; queue<pii> pool; struct edge { int to, nxt; } edges[MAX_M << 1]; void addpath(int src, int dst) { edges[current].to = dst, edges[current].nxt = head[src]; head[src] = current++; } int find(int id, int x) { return mem[id][x] == x ? x : mem[id][x] = find(id, mem[id][x]); } void dfs(int u, int color) { col[u] = color; for (int i = head[u]; i != -1; i = edges[i].nxt) if (col[edges[i].to] == 0) dfs(edges[i].to, -color); else if (col[edges[i].to] == color) tag[abs(color)] = true; } int main() { memset(head, -1, sizeof(head)); scanf("%d%d%d%s", &n, &m, &q, str + 1); for (int i = 1; i <= n; i++) mem[0][i] = mem[1][i] = i, travel[i][i] = true, pool.push(make_pair(i, i)); for (int i = 1; i <= m; i++) { scanf("%d%d", &ui[i], &vi[i]); if (str[ui[i]] == str[vi[i]]) addpath(ui[i], vi[i]), addpath(vi[i], ui[i]); } for (int i = 1; i <= n; i++) if (col[i] == 0) dfs(i, i); memset(head, -1, sizeof(head)), current = 0; for (int i = 1; i <= m; i++) { int u = ui[i], v = vi[i]; if (str[u] != str[v]) { if (find(0, u) == find(0, v)) continue; addpath(u, v), addpath(v, u); mem[0][find(0, u)] = find(0, v); } else { if (find(1, u) == find(1, v)) continue; addpath(u, v), addpath(v, u); mem[1][find(1, u)] = find(1, v), pool.push(make_pair(u, v)); } } for (int i = 1; i <= n; i++) if (tag[abs(col[i])] == true && already[abs(col[i])] == false) already[abs(col[i])] = true, addpath(i, i); // then we get to bfs; while (!pool.empty()) { pii u = pool.front(); pool.pop(); for (int i = head[u.first]; i != -1; i = edges[i].nxt) for (int j = head[u.second]; j != -1; j = edges[j].nxt) if (str[edges[i].to] == str[edges[j].to] && travel[edges[i].to][edges[j].to] == false) travel[edges[i].to][edges[j].to] = travel[edges[j].to][edges[i].to] = true, pool.push(make_pair(edges[i].to, edges[j].to)); } while (q--) { int u, v; scanf("%d%d", &u, &v), puts(travel[u][v] ? "YES" : "NO"); } return 0; }