基础
似乎也没什么好定义的,比较容易理解吧。主要思想就是给每一条边赋上一个字母,用经过的边来表示字符串,以此达到快速处理字符串前缀、后缀等问题。
放个图先,然后扔个代码并解释一下。

扩展应用
维护 最值
有一个集合 ,给定一个 ,求在序列 中的元素与 异或的最小值
比较简单,建 ,将 序列中所有元素转为二进制插入 中,然后将 转为二进制在 上匹配尽可能大的前缀。 的某位是 就尽量走 , 是 就尽量走 。
维护 的第 大
给定一个集合 ,给定一个 和集合中每个数异或一下,形成一个新集合 ,问 中第 大的值是多少
这个问题可以类比一下值域线段树查找第 大元素的问题。先把所有 转二进制,以最高位开头扔到 里。然后我们用一个 来存储经过点 的串的数量。根据 当前位上的数值来看看应该往哪边走,并记录一下路径。通过路径即可推出第 大异或值。
维护位运算最值
有一个集合 ,给出一个 ,查出 的最大值
主要是贪心的思想。
还是按照老办法把 扔到 里去。然后根据 当前位上的值来走。假如当前位为 就尽量走 ,反之随便走 ,问题来了,该怎么“随便走呢”?这里要用线段树合并的思想,还不会,挖个坑先。
经典例题
P3294 [SCOI2016]背单词(trie的生成树上dfs)
给你 个两两不同字符串,需要你给他们规定一个排列顺序。对于排列中的第 个字符串
如果存在一个字符串是它的后缀,并且不在它前面,那么费用增加
如果它的前面不存在一个是它的后缀,那么费用增加
如果前面存在一个是它的后缀,那么费用增加 (j是前面所有它的后缀中,最后的位置)
字符串总长
后缀不太好考虑,那么把字符串反转,后缀就变成前缀了。
贪心的来看这个题。我们应该尽可能地让一个串的前缀都出现在这个串的前面。那么,我们在 的生成树上
插入串的时候我们还是要记录每个节点的 ,为了使某个串的前缀尽量靠前,dfs时优先选择 大的节点走。dfs的时候也要记录下当前串输出时的编号,遇到一个串,就记录下它的贡献。
像处理这种数字的问题,还是转化为二进制插入 中。那么怎么处理查询呢?
考虑这样一个问题,当你在 树上查找你需要查找的那个串时,如果遇到一个节点,它被打过标记了,即有在这个节点上结尾的串,那么要想覆盖这个串,就需要一个长度比当前串长,且编号比这个串更大的串存在。这似乎就相当于一个单调栈,当你在 上查找串时,遇到一个比栈顶元素编号更大,且编号范围在 与 之间的串,就反复弹栈。查找结束的栈的栈内元素数量就是答案。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e6;
int n,m,cnt;
struct NODE{
int next[MAXN + 5][3],tot;
int exist[MAXN + 5];
void insert(string word,int num){
int i = 1;
for(int j = 0; j < word.size(); j++){
int now = word[j] - '0';
if(!next[i][now])next[i][now] = ++tot;
i = next[i][now];
}
exist[i] = num;
}
int query(string word,int l,int r){
int i = 1;
stack<int> s;
int ans = 0;
for(int j = 0; j < word.size(); j++){
int now = word[j] - '0';
if(!next[i][now])break;;
i = next[i][now];
if(exist[i]){
while(!s.empty() && s.top() > exist[i]){
if(s.top() >= l && s.top() <= r)ans--;
s.pop();
}
s.push(exist[i]);
if(exist[i] >= l && exist[i] <= r)ans++;
}
}
return ans;
}
}tree;
string change(int a){
string s;
while(a){
int x = a % 2;
a /= 2;
char c = x + '0';
s = c + s;
}
return s;
}
signed main(){
tree.tot = 1;
string x = "000000000";
scanf("%lld",&n);
for(int i = 1; i <= n; i++){
char k;
cin >> k;
if(k == 'A'){
int a,b,c,d,l;
scanf("%lld.%lld.%lld.%lld",&a,&b,&c,&d);
char cc;
cin >> cc >> l;
string A = change(a),B = change(b),C = change(c),D = change(d);
A = x.substr(0,8 - A.size()) + A;
B = x.substr(0,8 - B.size()) + B;
C = x.substr(0,8 - C.size()) + C;
D = x.substr(0,8 - D.size()) + D;
string s = A + B + C + D;
s = s.substr(0,l);
tree.insert(s,++cnt);
}
else{
int a,b,c,d,l,r;
scanf("%lld.%lld.%lld.%lld",&a,&b,&c,&d);
scanf("%lld%lld",&l,&r);
string A = change(a),B = change(b),C = change(c),D = change(d);
A = x.substr(0,8 - A.size()) + A;
B = x.substr(0,8 - B.size()) + B;
C = x.substr(0,8 - C.size()) + C;
D = x.substr(0,8 - D.size()) + D;
string s = A + B + C + D;
int ans = tree.query(s,l,r);
cout << ans << '\n';
}
}
}
__EOF__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具