【WC2013】糖果公园 树上莫队

本文介绍了一种基于树结构的莫队算法实现方法,通过将树分块并使用三关键字排序来优化查询效率。文章详细阐述了算法原理、关键步骤及其实现代码,适用于解决特定类型的数据查询问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树上莫队,将树分块,以x,y为一二关键字,以时间为第三关键字。暴力修改。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int N = 100005, B = 2500, D = 18;
struct arr { int x, y, n, z; } q[N], p[N];
struct Edge { int y, nex; } g[N * 2];
int n, col[N], W[N], cl, ti[N], top, st[N], Up[N][D], par[N], lp, lq, c[N], c1[N], h[N];
int pos[N], sz, u[N], m, Q;
LL sum, ans[N], V[N]; bool vis[N], Ty[N];
void Init(int x, int y) { g[++ sz] = (Edge) { y, pos[x] }, pos[x] = sz; }
void C(int x) {
    int c1 = c[x];
    if (vis[x]) sum -= V[c1] * W[ ti[c1] ], ti[c1] --;
    else ti[c1] ++, sum += V[c1] * W[ ti[c1] ];
    vis[x] ^= 1;
}
int Lca(int x, int y) {
    if (h[x] < h[y]) swap(x, y);
    Dwn(i, D - 1, 0) if ((1 << i) <= h[x] - h[y]) x = Up[x][i];
    if (x == y) return x;
    Dwn(i, D - 1, 0) if (Up[x][i] != Up[y][i]) x = Up[x][i], y = Up[y][i];
    return Up[x][0];
}
void Dfs(int x) {
    Up[x][0] = par[x]; u[x] = top;
    Rep(i, 1, D - 1) Up[x][i] = Up[ Up[x][i - 1] ][i - 1];
    RepE(i, x) {
        int y = g[i].y; if (y == par[x]) continue ;
        par[y] = x, h[y] = h[x] + 1; Dfs(y);
        if (top - u[x] > B) {
            cl ++; while (top > u[x]) col[ st[top --] ] = cl;
        }
    }
    st[++ top] = x;
}
void Change(int x, int y) { if (vis[x]) C(x), c[x] = y, C(x); else c[x] = y; }
void Modify(int x, int y) {
    if (h[x] < h[y]) swap(x, y);
    while (h[x] > h[y]) C(x), x = par[x];
    while (x != y) C(x), C(y), x = par[x], y = par[y];
}
bool cmp(arr a, arr b) {
    if (col[ a.x ] != col[ b.x ]) return col[ a.x ] < col[ b.x ];
    return (col[ a.y ] == col[ b.y ]) ? a.n < b.n : col[ a.y ] < col[ b.y ];
}
int main()
{
    scanf ("%d%d%d", &n, &m, &Q);
    Rep(i, 1, m) scanf ("%lld", &V[i]); // val
    Rep(i, 1, n) scanf ("%d", &W[i]); // time i
    Rep(i, 1, n - 1) {
        int x, y; scanf ("%d%d", &x, &y);
        Init(x, y), Init(y, x);
    }
    Rep(i, 1, n) scanf ("%d", &c[i]), c1[i] = c[i];
    Dfs(1); Rep(i, 1, top) col[ st[i] ] = cl;
    Rep(i, 1, Q) {
        int x, y; scanf ("%d%d%d", &Ty[i], &x, &y);
        if (Ty[i]) {
            if (col[x] > col[y]) swap(x, y);
            q[++ lq] = (arr) { x, y, i, 0 };
        } else {
            p[++ lp] = (arr) { x, y, i, c1[x] }, c1[x] = y;
        }
    }
    sort(q + 1, q + lq + 1, cmp);
    int t = 1, qu = 1, qv = 1;
    Rep(i, 1, lq) {
        int u = q[i].x, v = q[i].y, z = Lca(u, v);
        while (t <= lp && p[t].n < q[i].n) Change(p[t].x, p[t].y), t ++;
        while (t > 1 && p[t - 1].n > q[i].n) t --, Change(p[t].x, p[t].z);
        Modify(qu, u), Modify(qv, v);
        C(z), ans[ q[i].n ] = sum, C(z);
        qu = u, qv = v;
    }
    Rep(i, 1, Q) if (Ty[i]) printf("%lld\n", ans[i]);
 
    return 0;
}


### 关于树上莫队算法 #### 树上莫队算法简介 树上莫队算法是对经典莫队算法的一种扩展,用于处理树上的区间查询问题。其核心思想是在一棵树上维护动态区间的统计信息,并通过离线处理的方式减少重复计算的开销[^4]。 传统莫队算法主要用于一维数组中的区间查询问题,而树上莫队则将其推广到树形结构中。为了适应树的特性,通常会采用欧拉序(Euler Tour Traversal)或其他遍历方式将树转化为序列化形式,从而使得经典的莫队技巧得以应用。 --- #### 树上莫队算法的核心步骤 以下是树上莫队的主要实现思路: 1. **树的序列化** 使用深度优先搜索(DFS)对树进行遍历,记录下每个节点进入的时间戳 `in_time` 和退出的时间戳 `out_time`。这样可以通过时间戳范围 `[in_time[u], out_time[u]]` 来唯一表示以节点 `u` 为根的子树[^5]。 2. **分块策略** 将所有询问按照节点的时间戳分成若干块,每一块内的询问按特定顺序排列(通常是左端点所在块号升序、右端点降序)。这种排序方式可以显著降低移动指针的次数,从而提高效率[^6]。 3. **动态更新状态** 随着区间的变化,逐步加入或移除节点的影响。对于每次操作,都需要快速调整全局的状态变量(如计数器、频率表等),并实时更新答案。 4. **复杂度分析** 如果合理设计分块大小,则整体时间复杂度接近 \(O((N+Q)\sqrt{N})\),其中 \(N\) 是节点数量,\(Q\) 是询问数量[^7]。 --- #### 树上莫队的应用场景 树上莫队主要应用于涉及树结构的批量区间查询问题,尤其适合以下情况: - 查询条件较为复杂,无法通过简单的预处理完成。 - 数据规模较大,要求高效求解。 - 可接受一定的额外空间消耗以换取更快的速度。 具体例子包括但不限于: - 统计某个子树内满足某种性质的节点数目。 - 计算路径上的某些聚合属性(例如最大值、最小值、众数等)。 - 处理带权图上的最短路相关问题。 下面给出一段典型的 Python 实现代码作为参考: ```python from math import sqrt def tree_mos_algorithm(edges, queries): n = len(edges) + 1 adj_list = [[] for _ in range(n)] # 构建邻接表 for u, v in edges: adj_list[u].append(v) adj_list[v].append(u) # DFS 序列化 timestamp = [] parent = [-1] * n depth = [0] * n def dfs(node, par=-1, d=0): nonlocal timestamp parent[node] = par depth[node] = d timestamp.append(node) for neighbor in adj_list[node]: if neighbor != par: dfs(neighbor, node, d + 1) timestamp.append(node) root = 0 dfs(root) block_size = int(sqrt(len(timestamp))) sorted_queries = sorted( [(l, r, i) for i, (l, r) in enumerate(queries)], key=lambda x: ((timestamp[x[0]] // block_size), timestamp[x[1]]) ) result = [None] * len(sorted_queries) current_l = 0 current_r = -1 answer = 0 freq = {} def add(x): nonlocal answer freq[x] = freq.get(x, 0) + 1 if freq[x] == 1: answer += 1 def remove(x): nonlocal answer freq[x] -= 1 if freq[x] == 0: del freq[x] answer -= 1 for l, r, idx in sorted_queries: while current_l > l: current_l -= 1 add(timestamp[current_l]) while current_r < r: current_r += 1 add(timestamp[current_r]) while current_l < l: remove(timestamp[current_l]) current_l += 1 while current_r > r: remove(timestamp[current_r]) current_r -= 1 result[idx] = answer return result ``` --- #### 总结 树上莫队算法通过对树结构的有效转化和合理的分块策略,在大规模数据集下的性能表现优异。尽管其实现相对复杂,但在许多竞赛编程和工业级应用中具有重要价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值