1 条题解
-
0
题意分析
题目描述了一棵苹果树,树有N个分叉(节点),编号为1到N,其中1号节点是根节点。每个节点上最多有一个苹果。初始时,所有节点都有苹果。接下来有M个操作,操作分为两种:
C x
:改变节点x上的苹果状态。如果x有苹果,则摘掉;如果没有,则长出一个。Q x
:查询以x为根的子树中苹果的数量(包括x本身)。
解题思路
- 树的表示:首先需要用合适的数据结构表示这棵树。由于树的节点数可能很大(N ≤ 100,000),我们需要使用邻接表来存储树的结构。
- 子树查询:为了高效地查询子树中的苹果数量,我们需要对树进行某种遍历,使得子树在遍历序列中是连续的。通常,DFS序或欧拉序可以实现这一点。具体来说,我们可以通过后序遍历或前序遍历为每个节点分配一个进入时间(in)和离开时间(out),这样子树x的节点在序列中的位置就是in[x]到out[x]之间的连续区间。
- 区间求和:有了DFS序后,子树查询就转化为区间求和问题。单点更新和区间求和可以用树状数组(Fenwick Tree)或线段树(Segment Tree)来实现。由于树状数组实现简单且效率高,通常优先选择。
具体步骤
- 构建树结构:使用邻接表存储树。
- DFS遍历:为每个节点分配in和out时间戳。in[x]是第一次访问x的时间,out[x]是遍历完x的子树后离开的时间。子树x的节点在in[x]到out[x]之间。
- 初始化树状数组:初始时所有节点都有苹果,所以树状数组中每个in[x]的位置初始值为1。
- 处理操作:
C x
:改变x的苹果状态。如果x当前有苹果,将树状数组中in[x]位置的值减1;否则加1。Q x
:查询区间[in[x], out[x]]的和,即子树x的苹果数量。
标程
import sys from sys import stdin from collections import deque def main(): sys.setrecursionlimit(1 << 25) N = int(stdin.readline()) adj = [[] for _ in range(N+1)] for _ in range(N-1): u, v = map(int, stdin.readline().split()) adj[u].append(v) adj[v].append(u) in_time = [0]*(N+1) out_time = [0]*(N+1) time = 1 # 使用非递归DFS以避免递归深度问题 stack = [(1, None, True)] while stack: u, parent, is_first_visit = stack.pop() if is_first_visit: in_time[u] = time time += 1 stack.append((u, parent, False)) # 将子节点按顺序压入栈,这里按原始顺序逆序以保证顺序正确 for v in reversed(adj[u]): if v != parent: stack.append((v, u, True)) else: out_time[u] = time - 1 # 树状数组实现 class FenwickTree: def __init__(self, size): self.size = size self.tree = [0]*(self.size + 2) def update(self, index, delta): while index <= self.size: self.tree[index] += delta index += index & -index def query(self, index): res = 0 while index > 0: res += self.tree[index] index -= index & -index return res def range_query(self, l, r): return self.query(r) - self.query(l-1) ft = FenwickTree(N) # 初始化,所有节点都有苹果 apple = [1]*(N+1) for u in range(1, N+1): ft.update(in_time[u], 1) M = int(stdin.readline()) for _ in range(M): parts = stdin.readline().split() if parts[0] == 'Q': x = int(parts[1]) l = in_time[x] r = out_time[x] print(ft.range_query(l, r)) else: x = int(parts[1]) if apple[x] == 1: ft.update(in_time[x], -1) apple[x] = 0 else: ft.update(in_time[x], 1) apple[x] = 1 if __name__ == '__main__': main()
代码解释
- 输入处理:读取树的边并构建邻接表。
- DFS遍历:使用非递归DFS(避免递归栈溢出)为每个节点分配in和out时间戳,标记子树在序列中的区间。
- 树状数组:实现单点更新和区间查询功能。
- 初始化:初始时所有节点都有苹果,树状数组相应位置初始化为1。
- 处理操作:
Q x
:查询子树x的苹果数量,即查询区间[in[x], out[x]]的和。C x
:切换节点x的苹果状态,并更新树状数组。
这种方法确保了每次查询和更新操作的时间复杂度为O(log N),能够高效处理大规模数据。
- 1
信息
- ID
- 2322
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 3
- 已通过
- 1
- 上传者