首先 i,j 必须来自 u 的不同子树,这启发我们在树上进行启发式合并,使用 std::set 维护每棵子树的点集。每次合并两棵子树时,我们枚举较小子树的每一个点 x,在较大子树的 std::set 中查询分别查询 x 的前驱 pre 后继 net,将 (pre,x),(x,suf) 加入 u 所拥有的二元组。
这样我们就将二元组个数从 O(n2) 降到了 O(nlogn)!
By the Way:也可以使用 LCT 的 access 操作,求出 O(nlogn) 个二元组。详见 LOJ 6041 的算法三。
voiddfs_init(int u, int fu){ for (auto [v, w] : G.table[u]) { if (v == fu) continue; dist[v] = dist[u] + w; dfs_init(v, u); } }
std::set<int> g[N];
std::vector< std::pair<int, int> > scan[N];
std::vector< std::pair<int, int> > qry[N]; int ans[MaxQ];
int lst[N];
namespace BIT { int c[N];
voidadd(int x, int y){ for (; x <= n; x += x & -x) { c[x] += y; } }
intask(int x){ int ans = 0; for (; x; x -= x & -x) { ans += c[x]; } return ans; } }
voiddfs(int u, int fu){ g[u].insert(u); scan[u].push_back({u, id[u]});
for (auto [v, _] : G.table[u]) { if (v == fu) continue;
dfs(v, u);
if (g[u].size() < g[v].size()) { std::swap(g[u], g[v]); }
for (int x : g[v]) { auto it = g[u].lower_bound(x); if (it != g[u].end()) { scan[*it].push_back({x, id[u]}); } if (it != g[u].begin()) { it --; scan[x].push_back({*it, id[u]}); } } for (int x : g[v]) { g[u].insert(x); } // g[v].clear(); } }