Trie
Definition
A trie, also known as a prefix tree, is a tree-like data structure that resembles a dictionary.
Introduction
First, look at an image:

It can be observed that this trie uses edges to represent letters, and the path from the root node to any node on the tree represents a string. For example, \(1\to4\to 8\to 12\) represents the string caa.
The structure of a trie is very intuitive. We use \(\delta(u,c)\) to denote the next node pointed to by character \(c\) from node \(u\), or equivalently, the node representing the string formed by appending character \(c\) to the string represented by node \(u\). (The range of \(c\) depends on the size of the character set and is not necessarily \(0\sim 26\).)
Sometimes it is necessary to mark which strings have been inserted into the trie. This can be done by marking the node representing the string after each insertion.
Implementation
Here is a template encapsulated in a structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | |
Applications
String Retrieval
The most basic application of a trie—checking whether a string appears in the "dictionary."
Thus Begins the Wrong Roll Call
You are given \(n\) name strings, followed by \(m\) roll calls. For each call, you need to answer one of: "name does not exist", "first time calling this name", or "already called this name".
\(1\le n\le 10^4\), \(1\le m\le 10^5\), all strings have length at most \(50\).
Solution
Build a trie for all names, then query the trie to check if the string exists and whether it has been called before. Mark it as called on the first call.
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
Aho-Corasick Automaton
The trie is a part of the Aho-Corasick automaton.
Maintaining XOR Extremes
Treating the binary representation of numbers as strings allows building a trie with character set \(\{0,1\}\).
BZOJ1954 Longest XOR Path
Given a tree with weighted edges, find \((u, v)\) such that the XOR sum of edge weights on the path from \(u\) to \(v\) is maximized, and output this maximum value. The XOR sum refers to the XOR of all edge weights.
The number of nodes does not exceed \(10^5\), and edge weights are in \([0,2^{31})\).
Solution
Arbitrarily choose a root \(root\). Let \(T(u, v)\) denote the XOR sum of edge weights on the path between \(u\) and \(v\). Then \(T(u,v)=T(root, u)\oplus T(root,v)\), because the part above the LCA cancels out after XORing twice.
If all \(T(root, u)\) are inserted into a trie, for each \(T(root, u)\), we can quickly find the \(T(root, v)\) that maximizes the XOR sum with it:
Starting from the root of the trie, if possible, go to the subtree corresponding to the opposite bit of the current bit of \(T(root, u)\); otherwise, there is no choice.
The correctness of the greedy approach: If we go this way, this bit becomes \(1\); otherwise, it becomes \(0\). Higher bits should be prioritized to be as large as possible.
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | |
Maintaining XOR Sum
A 01-trie is a trie with character set \(\{0,1\}\). It can be used to maintain the XOR sum of some numbers, supporting modifications (deletion + reinsertion) and global increment by one (i.e., increment all maintained values by 1, which is essentially a special type of modification).
To maintain the XOR sum, the trie should be built from the least significant bit to the most significant bit.
A convention: In this context, "upward" refers to the path from the current node to the root, and "downward" refers to the subtree of the current node.
Insertion & Deletion
To maintain the XOR sum, we only need to know the parity of the counts of 0s and 1s at each bit. That is, for bit 1, the bit is 1 if and only if the number of 1s at this bit is odd. Remember this: if we are only maintaining the XOR sum, we only need to know the count of 1s at each bit, not the actual numbers maintained by the trie.
For each node, we need to maintain the following three quantities:
ch[o][0/1]refers to the two children of nodeo.ch[o][0]corresponds to the next bit being0, and similarlych[o][1]corresponds to the next bit being1.w[o]denotes the weight (count) on the edge from nodeoto its parent. For each inserted numberx, the weight on the path corresponding to the binary representation ofxin the trie is incremented by1.xorv[o]is the XOR sum maintained by the subtree rooted ato.
The code for maintaining node information is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The code for insertion and deletion is very similar.
Points to note:
-
Here,
MAXHrefers to the depth of the trie, i.e., forcing every leaf node to be at distanceMAXHfrom the root. For smaller values, such depth may not be necessary (e.g., inserting the number4, which is100in binary, only requires inserting001from the root). However, we force insertion toMAXHbits. This is to facilitate handling carry during global+1. For example, if the original number is3(11), after incrementing it becomes4(100). If only 2 bits were inserted for3, the carry would be lost. -
For insertion and deletion, only the
w[]of leaf nodes needs to be modified, and the information is maintained during backtracking.
Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
Global Increment by One
Global increment by one means adding 1 to all values maintained in the trie.
Formally, let the values maintained in the trie be \(V_1, V_2, V_3 \dots V_n\). After global increment by one, the values become \(V_1+1, V_2+1, V_3+1 \dots V_n+1\).
1 2 3 4 5 | |
Process
Consider how +1 operates in binary.
We only need to find the first 0 from the least significant bit to the most significant bit, change it to 1, and then change all 1s after this position to 0.
Here are some examples (numbers in parentheses are their decimal equivalents):
1 2 3 4 5 | |
Corresponding trie operations involve swapping left and right children and recursively operating along the swapped 0 edge.
Recall the definition of w[o]: w[o] denotes the weight (count) on the edge from node o to its parent.
Does this definition feel odd? It might be more intuitive to store the edge weights from the parent to its two children at the parent node. However, here, storing the distance to the parent at the child node is more convenient when swapping left and right children.
01-Trie Merging
This refers to merging two 01-tries while combining the maintained information.
There may be few articles about merging tries. Actually, merging tries is very similar to merging segment trees. You can search for "merging segment trees" to learn how to merge tries.
Merging tries is straightforward. Consider a function int merge(int a, int b) that takes the node indices of two tries at the same relative position and returns the node index after merging.
Process
How to implement it?
Consider three cases:
- If
adoes not have a node at this position, the merged node isb. - If
bdoes not have a node at this position, the merged node isa. -
If both
aandbexist, merge the information ofbintoa. The merged node isa, and then recursively process the children ofa.Hint: If the requirement is to merge
aandbinto a new tree, you can create a new node and merge into it. The code here only mergesb's information intoa.
Implementation
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
Actually, any trie can be merged. In other words, trie merging is not limited to 01-tries.
【luogu-P6018】【Ynoi2010】Fusion tree
You are given a tree with \(n\) nodes, each node has a weight. There are \(m\) operations. Support the following operations:
-
Increment the weights of all nodes at distance \(1\) from node \(x\) by \(1\). Here, the distance between two nodes on the tree is defined as the number of edges on the shortest path from one node to the other.
-
Decrease the weight of node \(x\) by \(v\).
-
Query the XOR sum of the weights of all nodes at distance \(1\) from node \(x\). For \(100\%\) of the data, \(1\le n \le 5\times 10^5\), \(1\le m \le 5\times 10^5\), \(0\le a_i \le 10^5\), \(1 \le x \le n\), \(opt\in\{1,2,3\}\). Ensure that the weight of each node is non-negative at all times.
Solution
Each node maintains a trie for the weights of its children. The trie should support global increment by one. Lazy tags can be used at each node to mark the increment amount for children's weights.
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | |
【luogu-P6623】【Provincial Selection Joint Exam 2020 A Volume】Tree
Given a rooted tree \(T\) with \(n\) nodes, nodes are numbered starting from \(1\), and the root is node \(1\). Each node has a positive integer weight \(v_i\).
Let the nodes in the subtree of node \(x\) (including \(x\) itself) be \(c_1,c_2,\dots,c_k\). Define the value of \(x\) as:
\(val(x)=(v_{c_1}+d(c_1,x)) \oplus (v_{c_2}+d(c_2,x)) \oplus \cdots \oplus (v_{c_k}+d(c_k, x))\) where \(d(x,y)\)
denotes the number of edges on the unique simple path between nodes \(x\) and \(y\) on the tree, and \(d(x,x) = 0\). \(\oplus\) denotes the XOR operation.
Please compute \(\sum\limits_{i=1}^n val(i)\).
Solution
Consider the contribution of each node to all its ancestors. Each node builds a trie, initially containing only its own weight. Then, merge the tries of all child nodes from bottom to top, perform a global increment by one, and then compute the answer.
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | |
Persistent Trie
See Persistent Trie.
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用