一开始我还想着用什么数据结构去维护的,想了好久没想出来。
其实看范围就应该想到n根号n能过的。
题意就是说每个点都有自己的颜色,给你q个操作,每次操作将这个点的颜色扩散给相邻点。问你q个操作之后每个点的颜色是什么。
首先我们暴力做复杂度是O(q*m)的这样肯定是不行的,于是我们想到优化。难道我们真的需要每个点都去暴力跑相邻点吗?
于是从这个点出发,如果一个点邻接点太多了,那么我们就用标记把这次操作O(1)化。假设我们设置这个太多的定义就是这个点的度超过了 lmt。
参考博客:https://www.cnblogs.com/Kanoon/p/15312156.html
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; const int inf = 0x3f3f3f3f; ///1061109567 const int maxn = 2e5 + 10; vector<int> G1[maxn]; ///邻接点 vector<int> G2[maxn]; ///度 >= lmt 的邻接点 pii ans[maxn], tag[maxn]; ///最终答案数组,标记数组 int main() { int n, m, q; cin >> n >> m >> q; int lmt = sqrt(2*m); ///设置lmt for (int i = 1; i <= m; ++ i) { int u, v; cin >> u >> v; G1[u].push_back(v); G1[v].push_back(u); } ///数组初始化 for (int i = 1; i <= n; ++ i) { ans[i] = tag[i] = {i, -1}; } ///预处理出所有点的邻接点中度大于lmt的点 for (int u = 1; u <= n; ++ u) { for (auto v : G1[u]) { if (G1[v].size() >= lmt) { G2[u].push_back(v); } } } for (int i = 1; i <= q; ++ i) { int u; cin >> u; ///用大于 lmt的邻接点去更新当前点 ///因为大于lmt的点我们是打标记,实际上我们并没有更新 ///所以当这个点拥有大于lmt的点时我们需要更新上来,跟之前暴力更新的值取大 /// O(2*m / lmt) for (auto v : G2[u]) { if (ans[u].second < tag[v].second) { ans[u] = tag[v]; } } /// O(lmt) if (G1[u].size() < lmt) { ///当前点度小于lmt,暴力更新 for (auto v : G1[u]) { ans[v] = {ans[u].first, i}; } } else { ///大于lmt,打上标记 tag[u] = {ans[u].first, i}; } ///所以复杂度为 O(q * (2*m / lmt + lmt)) } ///由于小于lmt的点我们已经暴力更新了,剩下的只需要用大于lmt的点去更新当前答案即可 for (int u = 1; u <= n; ++ u) { for (auto v : G2[u]) { if (ans[u].second < tag[v].second) { ans[u] = tag[v]; } } cout << ans[u].first << " "[u == n]; } return 0; }