Trie 的一类应用
\(\text{Trie}\)
先从 [十二省联考 \(2019\)] 异或粽子 谈起
不难想到堆加可持久化 \(Trie\) 的做法
这就和 \(\text{[NOI2010]}\) 超级钢琴 类似了
这种做法与 \(k\) 有关,并且很容易推及许多关于 \(\text{kth}\) 的问题
\(O(k \log n)\)
然而就此问题而言,还有与 \(k\) 无关的做法
思路还算好想
先求出 \(kth\) 的值 \(val\)
这是第一步,对应 \(\text{CF1055F Tree and XOR}\)
\(O(n \log V)\)
$\text{Code}$
#include <cstdio>
#include <iostream>
#define RE register
#define IN inline
typedef long long LL;
using namespace std;
const int N = 1e6 + 5;
int n, p1[N], p2[N], tr[N][2], siz[N];
LL k, a[N], ans;
IN LL read()
{
LL x = 0; char ch = getchar(); int f = 1;
for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
return x * f;
}
int main()
{
n = read(), k = read(), p1[1] = p2[1] = 1; LL y;
for(RE int i = 2, x; i <= n; i++)
x = read(), y = read(), a[i] = a[x] ^ y, p1[i] = p2[i] = 1;
for(RE int i = 61; i >= 0; i--)
{
for(RE int j = 1; j <= n + 1; j++) tr[j][0] = tr[j][1] = siz[j] = 0;
int tot = 1, x = 0; LL sum = 0;
for(RE int j = 1; j <= n; j++)
{
int c = (a[j] >> i) & 1;
if (!tr[p1[j]][c]) tr[p1[j]][c] = ++tot;
++siz[p1[j] = tr[p1[j]][c]];
}
for(RE int j = 1; j <= n; j++) sum += siz[tr[p2[j]][(a[j] >> i) & 1]];
if (sum < k) x = 1, k -= sum, ans |= (1LL << i);
for(RE int j = 1; j <= n; j++) p2[j] = tr[p2[j]][(a[j] >> i) & 1 ^ x];
}
printf("%lld\n", ans);
}
然后在 \(Trie\) 上跑,统计当 \(val\) 某一位为 \(0\) 时
各个 \(a_i\) 与其它数的异或值有多少在这一位为 \(1\) 从而大于 \(val\)
统计这些异或值的和
使这一位为 \(1\) 的数在 \(Trie\) 上位于一棵子树中
统计异或值的和当然可以分位算,这要只要记录子树中某一位为 \(0,1\) 的数的数量
可以开成与 \(Trie\) 结点数相当并附加两维大小为位数与 \(01\) 来统计数量
然而更好的做法是:
注意到 \(Trie\) 中同一子树中的数是按大小连续的
可以先将 \(a\) 排序,前缀和统计每位为 \(01\) 的数量
空间开销更小
这个做法对应:\(\text{CF241B Friends}\)
\(O(n\log V+n\log^2 V)\)
$\text{Code}$
#include <cstdio>
#include <algorithm>
#define IN inline
typedef long long LL;
using namespace std;
const int P = 1e9 + 7, N = 50003, inv2 = 5e8 + 4;
int n, size = 1;
int tr[N * 31][2], siz[N * 31], a[N], tmp[N], num[N][31][2], L[N * 31], R[N * 31];
LL m;
IN void Add(LL &x, LL y) {if ((x += y - P) < 0) x += P;}
IN void insert(int id, int x) {
int u = 1;
for(int i = 30; i >= 0; i--) {
int c = (x >> i) & 1;
if (!tr[u][c]) tr[u][c] = ++size, L[size] = id;
u = tr[u][c], ++siz[u], R[u] = id;
}
}
IN int query(LL &k) {
for(int i = 1; i <= n; i++) tmp[i] = 1;
int res = 0;
for(int j = 30; j >= 0; j--) {
LL sum = 0; int x = 0;
for(int i = 1; i <= n; i++) sum += siz[tr[tmp[i]][(a[i] >> j) & 1 ^ 1]];
if (sum >= k) res |= (1 << j), x = 1; else k -= sum;
for(int i = 1; i <= n; i++) tmp[i] = tr[tmp[i]][(a[i] >> j) & 1 ^ x];
}
return res;
}
IN void solve(int kth) {
for(int i = 1; i <= n; i++) tmp[i] = 1;
LL res = m * kth % P;
for(int j = 30; j >= 0; j--) {
int kb = ((kth >> j) & 1);
if (!kb)
for(int i = 1; i <= n; i++) {
int c = tr[tmp[i]][(a[i] >> j) & 1 ^ 1];
for(int k = 0, z; k <= 30; k++)
if(L[c]) Add(res, (1LL << k) * (num[R[c]][k][z = ((a[i] >> k) & 1 ^ 1)] - num[L[c] - 1][k][z]) % P);
}
for(int i = 1; i <= n; i++) tmp[i] = tr[tmp[i]][(a[i] >> j) & 1 ^ kb];
}
printf("%lld\n", res * inv2 % P);
}
int main() {
scanf("%d%lld", &n, &m), m <<= 1;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) insert(i, a[i]);
for(int i = 1; i <= n; i++)
for(int j = 0; j <= 30; j++){
for(int k = 0; k < 2; k++) num[i][j][k] = num[i - 1][j][k];
++num[i][j][a[i] >> j & 1];
}
solve(query(m));
}
对于 异或粽子 这题我采用了上述做法(原因是堆加可持久化 \(Trie\) 调不出来。。。
$\text{Code}$
#include <cstdio>
#include <algorithm>
#include <iostream>
#define IN inline
typedef long long LL;
using namespace std;
const int N = 500003, LG = 32;
int n, size = 1;
int tr[N * 33][2], siz[N * 33], tmp[N], num[N][33][2], L[N * 33], R[N * 33];
LL a[N], m;
IN void insert(int id, LL x) {
int u = 1;
for(int i = LG; i >= 0; i--) {
int c = (x >> i) & 1;
if (!tr[u][c]) tr[u][c] = ++size, L[size] = id;
u = tr[u][c], ++siz[u], R[u] = id;
}
}
IN LL query(LL &k) {
for(int i = 1; i <= n; i++) tmp[i] = 1;
LL res = 0;
for(int j = LG; j >= 0; j--) {
LL sum = 0; int x = 0;
for(int i = 1; i <= n; i++) sum += siz[tr[tmp[i]][(a[i] >> j) & 1 ^ 1]];
if (sum >= k) res |= (1LL << j), x = 1; else k -= sum;
for(int i = 1; i <= n; i++) tmp[i] = tr[tmp[i]][(a[i] >> j) & 1 ^ x];
}
return res;
}
IN void solve(LL kth) {
for(int i = 1; i <= n; i++) tmp[i] = 1;
LL res = m * kth;
for(int j = LG; j >= 0; j--) {
int kb = ((kth >> j) & 1);
if (!kb)
for(int i = 1; i <= n; i++) {
int c = tr[tmp[i]][(a[i] >> j) & 1 ^ 1];
for(int k = 0, z; k <= LG; k++)
if (L[c]) res += (1LL << k) * (num[R[c]][k][z = ((a[i] >> k) & 1 ^ 1)] - num[L[c] - 1][k][z]);
}
for(int i = 1; i <= n; i++) tmp[i] = tr[tmp[i]][(a[i] >> j) & 1 ^ kb];
}
printf("%lld\n", res / 2);
}
IN void read(LL &x) {
x = 0; int f = 1; char ch = getchar();
for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
x *= f;
}
int main() {
scanf("%d", &n), read(m), m <<= 1, ++n;
for(int i = 2; i <= n; i++) read(a[i]), a[i] ^= a[i - 1];
sort(a + 1, a + n + 1);
for(int i = 1; i <= n; i++) insert(i, a[i]);
for(int i = 1; i <= n; i++)
for(int j = 0; j <= LG; j++){
for(int k = 0; k < 2; k++) num[i][j][k] = num[i - 1][j][k];
++num[i][j][a[i] >> j & 1];
}
solve(query(m));
}
当然堆的做法是很好的,超级钢琴 这题就很好用
不过要把可持久化 \(Trie\) 换成主席树(按理说这并不符合本文标题
$\text{Code}$
#include <cstdio>
#include <queue>
#include <iostream>
#define IN inline
#define RE register
using namespace std;
typedef long long LL;
const int N = 5e5 + 5, M = N * 33;
int n, k, L, R, s[N], Mn, Mx, rt[N];
struct node{
int x, k, s;
IN bool operator < (const node &a) const {return s < a.s;}
};
priority_queue<node> Q;
struct ChairmanTree{
int size, ls[M], rs[M], siz[M];
IN void insert(int &x, int y, int v, int l, int r)
{
siz[x = ++size] = siz[y] + 1, ls[x] = ls[y], rs[x] = rs[y];
if (l == r) return;
int mid = l + r >> 1;
if (v <= mid) insert(ls[x], ls[y], v, l, mid);
else insert(rs[x], rs[y], v, mid + 1, r);
}
IN int Query(int x, int y, int k, int l, int r)
{
if (l == r) return l;
int mid = l + r >> 1, s;
if ((s = siz[ls[x]] - siz[ls[y]]) >= k) return Query(ls[x], ls[y], k, l, mid);
return Query(rs[x], rs[y], k - s, mid + 1, r);
}
}T;
IN void read(int &x)
{
x = 0; int f = 1; char ch = getchar();
for(; !isdigit(ch); f = (ch == '-' ? -1 : f), ch = getchar());
for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
x *= f;
}
IN node Query(int x, int y)
{
int u = rt[x - L], v = rt[max(x - R - 1, 0)];
if (T.siz[u] - T.siz[v] < y) return node{0, 0, 0};
return node{x, y, s[x] - T.Query(u, v, y, Mn, Mx)};
}
int main()
{
read(n), read(k), read(L), read(R), Mn = 1e9, Mx = -1e9;
for(RE int i = 2; i <= n + 1; i++) read(s[i]), s[i] += s[i - 1], Mn = min(Mn, s[i]), Mx = max(Mx, s[i]);
T.insert(rt[1], 0, 0, Mn, Mx);
for(RE int i = 2; i <= n + 1; i++) T.insert(rt[i], rt[i - 1], s[i], Mn, Mx);
for(RE int i = L + 1; i <= n + 1; i++) Q.push(Query(i, 1));
LL ans = 0;
for(; k; k--)
{
node now = Q.top(); Q.pop(), ans += now.s, now = Query(now.x, now.k + 1);
if (now.x) Q.push(now);
}
printf("%lld\n", ans);
}
而在测试时发现神奇的东西:
开 \(\text{O2}\) ,若有未定义行为(如访问未初始化内存),则会出错
而数组越界,有时会变得无影响,有时会公正地让你 \(WA\) 掉
所以初始化,数组边界要非常重视
自然溢出可以用,其他类型的溢出要慎重
Trie 的一类应用的更多相关文章
- 从Trie树(字典树)谈到后缀树
转:http://blog.csdn.net/v_july_v/article/details/6897097 引言 常关注本blog的读者朋友想必看过此篇文章:从B树.B+树.B*树谈到R 树,这次 ...
- 【trie树】【P4551】 最长异或路径
Description 给定 \(n\) 个点的带边权树,求一条异或和最大的简单路径 Input 第一行是点数 \(n\) 下面 \(n - 1\) 行每行三个整数描述这棵树 Output 输出一个数 ...
- Trie树入门
Trie树入门 貌似很多人会认为\(Trie\)是字符串类型,但是这是数据结构!!!. 详情见度娘 下面开始进入正题. PS:本文章所有代码未经编译,有错误还请大家指出. 引入 先来看一个问题 给 ...
- [note]一类位运算求最值问题
[note]一类位运算求最值问题 给定一些数,让你从中选出两个数a,b,每次询问下列中的一个 1.a and b的最大值 2.a xor b的最大值 3.a or b的最大值 神仙们都是FWT,小蒟蒻 ...
- [算法]从Trie树(字典树)谈到后缀树
我是好文章的搬运工,原文来自博客园,博主July_,地址:http://www.cnblogs.com/v-July-v/archive/2011/10/22/2316412.html 从Trie树( ...
- 数据结构篇——字典树(trie树)
引入 现在有这样一个问题, 给出\(n\)个单词和\(m\)个询问,每次询问一个单词,回答这个单词是否在单词表中出现过. 好像还行,用 map<string,bool> ,几行就完事了. ...
- 『异或粽子 堆 可持久化trie』
异或粽子 Description 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子. 小粽面前有 n 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 1 到 n.第 i 种馅儿 ...
- 【数据结构】关于前缀树(单词查找树,Trie)
前缀树的说明和用途 前缀树又叫单词查找树,Trie,是一类常用的数据结构,其特点是以空间换时间,在查找字符串时有极大的时间优势,其查找的时间复杂度与键的数量无关,在能找到时,最大的时间复杂度也仅为键的 ...
- Trie 前缀树或字典树 确定有限状态自动机
https://zh.wikipedia.org/wiki/Trie 应用 trie树常用于搜索提示.如当输入一个网址,可以自动搜索出可能的选择.当没有完全匹配的搜索结果,可以返回前缀最相似的可能.[ ...
- Codeforces 840E - In a Trap(树分块+trie)
Codeforces 题面传送门 & 洛谷题面传送门 一道非常精彩,同时也很经典的题目.和这场的 C 一样经典 首先看到这个数据范围先猜正解复杂度:\(n\) 级别大于 \(q\),所以大概是 ...
随机推荐
- 本地JS文件批量压缩
最近在维护一个小后台项目,有段JS需要压缩上传到CDN存储服务器.由于之前压缩的JS文件都比较少,都是手动压缩的.这次需要压缩的文件比较多,所以用了批量压缩.特此记录一下,方便大家和自己以后再用到的时 ...
- 如何用3D流体实现逼真水流效果?
华为应用市场在2022年HDC大会期间发布了一款3D水流主题,基于华为HMS Core Scene Kit服务能力,展现立体灵动的水流岛屿,可跟随用户指尖实现实时流体波动效果,既趣味又解压. 让变幻莫 ...
- vscode问题:由于找不到ffmpag.dll文件,无法继续执行代码
工作中发现VS code打不开了,显示如下: 解决方法: 一.打开Microsoft VS Code 文件夹,发现一部分文件被打包进了一个叫"_"的文件夹(第一个) 二.把该文 ...
- 【世界杯黑技术专题】「原理探索专题」一文解读一下“卡塔尔世界杯”的先进技术之半自动越位技术SAOT+比赛用球Al Rihla
盘点卡塔尔世界杯的先进黑科技 归纳总结一下目前世界杯的先进的黑科技有哪些?大致可以划分为以下几点. 半自动化越位技术 比赛用球Al Rihla 球场智能空调 可持续利用的体育场 便利的数字设施和App ...
- Pytorch基础-张量基本操作
一,张量的基本操作 二,维度变换 2.1,squeeze vs unsqueeze 维度增减 2.2,transpose vs permute 维度交换 三,索引切片 3.1,规则索引切片方式 3.2 ...
- uniapp小程序使用高德地图api实现路线规划
路线规划 简介 路线规划常用于出行路线的提前预览,我们提供4种类型的路线规划,分别为:驾车.步行.公交和骑行,满足各种的出行场景. 高德开放平台 本例是驾车路线规划功能和位置选择地图api:choos ...
- CodeForces 构造题专项解题报告
CodeForces 构造题专项解题报告 \(\newcommand \m \mathbf\)\(\newcommand \oper \operatorname\) \(\text{By DaiRui ...
- 蓝桥真题——最短路 & 门牌制作
题目1 最短路 标签:填空题 2019 省赛 如下图所示,G 是一个无向图,其中蓝色边的长度是 1.橘色边的长度是 2.绿色边的长度是 3. 则从 A 到 S 的最短距离是多少? 答案 由图可得,最短 ...
- 手把手教你写Dockerfile以及测试
Dockerfile是什么? dockerfile就是用来构建docker镜像的构建文件,命令参数脚本. 如何使用Dockerfile? 1.编写一个Dockerfile文件 2.docker bui ...
- three.js一步一步来--如何画出一根线
下面是画出线的代码,可以参考一下哟~~ <template> <div style="width:1000px; height:800px"> <p& ...