首页 > 其他分享 >01字典树和可持久化01字典树

01字典树和可持久化01字典树

时间:2024-07-02 16:19:54浏览次数:20  
标签:cnt 01 持久 int son ans 字典

01字典树

01字典树是一种只有0和1两种边的字典树。可以解决查询第 \(k\) 小,查询 \(x\) 是第几小等问题。

查询第 \(k\) 小

可以把输入的数转成等长二进制,然后插入01字典树。比如将 \([0,0,1,3,3]\) 插入字典树:

这里红色数字表示以该段为前缀的数的个数,黑色表示对应的数。

假设我们要查询第 \(3\) 小,可以发现根节点的左子树里有 \(3\) 个数,即第 \(3\) 小在左子树内:

接着看,左子树内只有两个数,说明答案在右子树内:

最终我们就得到了答案 \(1\)。

观察这个过程,可以发现第 \(k\) 小的求法:

  • 当左子树内的数的个数 \(cnt< k\),则答案在右子树内,\(k\leftarrow k-cnt\)。
  • 否则答案在左子树内。

不断递归上述过程,直到找到答案。

空间复杂度 \(O(N \log \max \{a_i\})\),单次查询时间复杂度 \(O(\log \max \{a_i\})\)。

代码

int GetNum(int k) {
    int u = 1, ans = 0;
    for(int i = 29; i >= 0; --i) {
      if(cnt[son[u][0]] < k) {
        k -= cnt[son[u][0]], u = son[u][1], ans = (ans << 1) | 1;
      }else {
        u = son[u][0], ans <<= 1;
      }
    }
    return ans;
  }

GetNum(k);

查询 \(x\) 为第几小

这个很容易明白,就不多做解释了。

重复执行以下过程:

  • 如果 \(x\) 在右子树,则 \(ans \leftarrow ans+cnt\),其中 \(ans,cnt\) 分别为答案和左子树的数的数量。
  • 否则什么也不做。
  • 走到对应的儿子上。

最终的答案就为 \(ans+1\)。

空间复杂度 \(O(N \log \max \{a_i\})\),单次查询时间复杂度 \(O(\log \max \{a_i\})\)。

代码

int GetRnk(int x) {
  int u = 1, ans = 1;
  for(int i = 29; i >= 0; --i) {
    if((x >> i) & 1) {
      ans += cnt[son[u][0]], u = son[u][1];
    }else {
      u = son[u][0];
    }
  }
  return ans;
}

GetRnk(x);

\(x\) 与其中一数的最大异或和

贪心地考虑即可:

  • 如果当前这位存在与 \(x\) 的不同的,则选择这一位
  • 否则不选择

空间复杂度 \(O(N \log \max \{a_i\})\),单次查询时间复杂度 \(O(\log \max \{a_i\})\)。

代码

int GetMaxXor(int x) {
  int u = 1, ans = 0;
  for(int i = 29; i >= 0; --i) {
    if(cnt[son[u][!((x >> i) & 1)]) {
      u = son[u][!((x >> i) & 1)], ans = (ans << 1) | 1;
    }else {
      u = son[u][(x >> i) & 1], ans <<= 1;
    }
  }
  return ans;
}
           
GetMaxXor(x);

01字典树还有很多用处,这里就不多说了。

可持久化01字典树

可持久化01字典树和01字典树只有一个区别,它可以查询任意时刻的01字典树。

首先,我们能想到的最暴力的方法就是每次修改都复制一颗字典树。虽然这样很明显空间会爆掉,但我们还是先看看。比如在 \([0,0,1,3,3]\) 中再加入一个 \(2\):

你可能会注意到,其中大部分的点都没有变化,只有处于 \(2\) 的这条链上有变化,我们不妨这么做:

可以发现现在总共只加入了 \(O(\log \max \{a_i\})\) 个点!

而可持久化字典树也正是这样运作的,就是把不用修改的子树直接指向之前的。在查找历史版本时直接从对应的根节点搜下去就行了。

空间复杂度 \(O((N+M) \log \max \{a_i\})\),单次插入时间复杂度 \(O(\log \max \{a_i\})\)。

代码

void Insert(int x, int id, int pos, int val) {
  ROOT[id] = ++tot;
  int u = tot, v = ROOT[x];
  for(int i = 19; i >= 0; --i) {
    son[u][(pos >> i) & 1] = ++tot;
    son[u][!((pos >> i) & 1)] = son[v][!((pos >> i) & 1)];
    u = son[u][(pos >> i) & 1], v = son[v][(pos >> i) & 1];
  }
  cnt[u] = val;
}

Insert(x, y, pos, val);

查询区间第 \(k\) 小

01字典树只能求全体第 \(k\) 小,但无法求区间第 \(k\) 小。而区间第 \(k\) 小也很简单,即两个前缀字典树相减再查询。

空间复杂度 \(O((N+M) \log \max \{a_i\})\),单次查询时间复杂度 \(O(\log \max \{a_i\})\)。

代码

int GetNum(int l, int r, int k) {
  int u = ROOT[r], v = ROOT[l - 1], ans = 0;
  for(int i = 29; i >= 0; --i) {
   	if(cnt[son[u][0]] - cnt[son[v][0]] < k) {
     k -= cnt[son[u][0]] - cnt[son[v][0]], u = son[u][1], v = son[v][1], ans = (ans << 1) | 1;
    }else {
     u = son[u][0], v = son[v][0], ans <<= 1;
    }
  }
  return ans;
}

GetNum(l, r, k);

查询 \(x\) 是区间第几小

两个前缀字典树相减再查询即可。

空间复杂度 \(O((N+M) \log \max \{a_i\})\),单次查询时间复杂度 \(O(\log \max \{a_i\})\)。

代码

int GetRnk(int l, int r, int x) {
  int u = ROOT[r], v = ROOT[l - 1], ans = 1;
  for(int i = 29; i >= 0; --i) {
    if((x >> i) & 1) {
      ans += cnt[son[u][0]] - cnt[son[v][0]], u = son[u][1], v = son[v][1];
    }else {
      u = son[u][0], v = son[v][0];
    }
  }
  return ans;
}

GetRnk(l, r, x);

标签:cnt,01,持久,int,son,ans,字典
From: https://www.cnblogs.com/yaosicheng124/p/18280075

相关文章

  • 可持久化 0/1 Trie
    可持久化可以维护数据结构的历史版本。对于一个字典树,如果更改一个元素,暴力做法是复制一个树,让后在树上修改。其实,这样是有很多个一定一样的点是浪费的,真正被修改的是\(\log_2n\)个点,\(2\log_2n\)条边。优点:大大减低时间复杂度,还支持在线做。缺点:不能传懒......
  • HAL库使用SPI协议修改MCP41010数字电位器阻值
    MCP41010MCP41010-I/SN是采用8引脚SOIC封装的8位分辨率单通道易失数字电位器。抽头的位置呈线性变化,并通过行业标准SPI接口进行控制。MCP41010的电阻值为10Kohm,具有出色的交流和直流特性,在静态工作期间的功耗小于1?A。提供了软件关闭功能,该功能可将“A”端子与电阻器堆栈断......
  • Hadoop权威指南-读书笔记-01-初识Hadoop
    Hadoop权威指南-读书笔记记录一下读这本书的时候觉得有意思或者重要的点~第一章—初识HadoopTips:这个引例很有哲理嘻嘻......
  • CH01_初识JavaScript
    第1章:初识JavaScript编程语言本章目标了解为什么要学习JavaScipt编程语言掌握JS的基本结构掌握JS的执行原理掌握JS的基本语法结构掌握JS的几种输出方式掌握JS的注释课程回顾什么是HTML?HTML的标签分为块级元素和行级元素,他们的区别是什么?HTML的表单元素有那些?HTML的列表......
  • BUUCTF刷题:[DDCTF 2019]homebrew event loop
    [DDCTF2019]homebreweventloop代码审计fromflaskimportFlask,session,request,Responseimporturllibimporturllib.parseapp=Flask(__name__)app.secret_key='*********************'#censoredurl_prefix='/d5afe1f66147e857'd......
  • P6754 [BalticOI 2013 Day1] Palindrome-Free Numbers
    数据范围一眼数位dp。关键条件为如果一个数字串的一个长度大于 11 的子串也为回文串的话,那么我们也定义这个数字串为回文串。仔细思考发现一旦两个连续的数相同(偶回文)或两个数隔一个数相同(奇回文)都是回文,所以要保证连续三个数不相同,记录前两位即可。注意事项:1.前导零不应为0......
  • day01 常量和变量
    go常量与变量拓展常量packagemainimport( "fmt" "reflect")varc3intfuncmain(){ //变量定义公式:var变量名数据类型=值 //1.go语言中定义变量:通过var关键字定义变量,后面跟上变量的类型 varnamestring="张阿三" varageint=20 fmt.Pri......
  • 20240701-薇薇的梦越来越奇怪了
    教练,我想玩原神了。昨晚梦到毕业旅行还是毕业晚会的晚上,看到rlh在玩原神。然后我站在旁边看他玩原神。还有个很离谱的。5月24号那天晚上,雷雨交加,我睡不着,翻来覆去在想之前和苏泊尔还有卡先生的一次谈话。想着想着,意识变得模糊了。然后开始做梦,而且好像是顺着我当时的......
  • 339 Refresh Tokens 01(生成RefreshToken)
    步骤1、appsettings.json"Jwt":{"Issuer":"http://localhost:7221","Audience":"http://localhost:4200","EXPIRATION_MINUTES":1,"Key":"thisissecretkeyforjwtthisisse......
  • CF950Div3 G. Yasya and the Mysterious Tree(01Trie)
    Problem题目地址Solution设\(s[u]\)是根到\(u\)路径上的异或和,树上任意两点\(u,v\)的路径异或和可表示为\(s[u]\opluss[v]\)。考虑查询操作?vx即求\(\max\{s[v]\opluss[u]\oplusx|\\1\leu\len,u\not=v\}\),若把\(s[v]\oplusx\)看作一个整体......