您的位置:首页 > 其它

论可持久化Trie树贪心在区间最大最小值的运用

2017-09-28 16:30 260 查看
可持久化01Trie树主要解决的是最大异或和问题, 譬如BZOJ3261BZOJ2741
都是01Trie树的最大异或和的运用. 在做01Trie的时候我们贪心的尽量选与当前这一位不一样的使得高位异或下来最大. 而同样的, 我们可以建01Trie来解决区间最大最小值问题, 对于序列上每个点建一个可持久化Trie, 第i点上的01Trie就维护1 ~ i 的每个数, 把每个数二进制拆分插进01trie里. 每次询问l, r的时候就查询l-1的trie树和r的trie树, 从根开始往下, 如果当前可以选1的话就选1(选1的条件就是经过r的trie树这个点之后的数比l-1的多, 这就说明l-r的区间里有这个数,
那么就可以选, 这与区间第k大值的域线段树相减同理). 这样贪心的去选最后就一定是最大的.

综上策略, 我们就可以得到nlog的算法, 而且这个log是值域的log(我们是把每个数二进制拆分插进01trie里). 也就是说当n特别大, 但是值域很小的时候, 我们可以得到一个时间复杂度碾压线段树, 空间复杂度碾压st表的算法. 譬如n是1e9, 但值域只有1000, 那么线段树的理论复杂度就是01trie的3倍, 当然1e9的n的空间已经是难以接受的了, 这里只是举一个例子.

下面是求区间最大值的代码, 值域在1000左右.

#include<stdio.h>
const int maxbit = 10;
const int maxn = 1000005;
int id, n, q, wanna, ed;
int c[maxn * maxbit][2], tr[maxn], sum[maxn * maxbit];
inline const int read(){
register int x = 0;
register char ch = getchar();
while(ch < '0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
return x;
}
void insert(int &rt,int pre,int who,int pos){
rt = ++id;
c[rt][0] = c[pre][0]; c[rt][1] = c[pre][1];
(!sum[rt]) ? sum[rt] = sum[pre] + 1 : sum[rt]++;
if(pos == -1) return;
wanna = (who >> pos) & 1;
insert(c[rt][wanna], c[pre][wanna], who, pos - 1);
}
inline int query(int l,int r, int opt){
ed = 0;
for(int i = maxbit; i >= 0 ; --i){
if(sum[c[r][1]] > sum[c[l][1]]) ed |= (1 << i), l = c[l][1], r = c[r][1];
else l = c[l][0], r = c[r][0];
}
return ed;
}
int main(){
n = read(), q = read();
int opt, x, y;
for(register int i = 1; i <= n; ++i) x = read(), insert(tr[i], tr[i - 1], x, maxbit);
for(register int i = 1; i <= q; ++i) printf("%d\n", query(tr[x - 1], tr[y], opt));
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐