Nikitosh 和异或 —— 一道 trie 树的题用可持久化 trie 水 然后翻车了...

题意简介

题目就是叫你找两个不重合的非空区间,使得这两个区间里的数异或后相加的和最大

(看到异或,没错就决定是你了可持久化trie!)

思路

水一波字典树,莫名觉得这题可持久化能过,于是水了一发挂了,造了一波数据,然后发现是自己在做完一遍可持久化之后cnt 没有清零....

其实要用可持久化trie 来做的话也就是常规操作(话说普通字典树不也是常规操作?)

也就是前缀和往可持久化trie 上update , 然后每个 L[i]、R[i] 记录当前点为右(左)区间的最大区间异或和

然后就是枚举断点了,考虑我们枚举到的断点前的那个区间其实是确定的(异或和最大的那个),

那么我们拿当前断点作为第二个 区间的左端点,前面的区间由 lef 变量不断更新,最后就能累加出答案。

于是没什么好说的了,板子题。

代码如下

 1 //by Judge
 2 #include<iostream>
 3 #include<cstdio>
 4 using namespace std;
 5 const int M=4e5+111;
 6 //#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
 7 char buf[1<<21],*p1=buf,*p2=buf;
 8 inline int read(){
 9     int x=0,f=1; char c=getchar();
10     for(;!isdigit(c);c=getchar()) if(c==‘-‘) f=-1;
11     for(;isdigit(c);c=getchar()) x=x*10+c-‘0‘; return x*f;
12 }
13 int n,cnt,a[M],L[M],R[M];
14 int d[35],rt[M<<2],son[M<<5][2],sum[M<<5];
15 inline void split(int k){ //换二进制
16     int i,len=0;
17     while(k) d[++len]=k&1,k>>=1;
18     for(int i=len+1;i<=31;++i) d[i]=0;
19 }
20 inline void update(int& now,int las){ //可持久化的更新
21     sum[now=++cnt]=sum[las]+1;
22     int i,tmp=now;
23     for(i=31;i;--i){
24         son[tmp][d[i]^1]=son[las][d[i]^1],
25         son[tmp][d[i]]=++cnt,las=son[las][d[i]],
26         sum[tmp=cnt]=sum[las]+1;
27     }
28 }
29 inline int query(int u,int v){ //询问区间内与当前数的最大异或和
30     int ans=0,i;
31     for(i=31;i;--i){
32         if(sum[son[v][d[i]^1]]-sum[son[u][d[i]^1]]>0)
33             ans|=(1<<i-1),u=son[u][d[i]^1],v=son[v][d[i]^1];
34         else u=son[u][d[i]],v=son[v][d[i]];
35     } return ans;
36 }
37 int main(){  //分函数都是常规操作(因为我都是直接搞了自己的板子)
38     int x,lef,res=0;
39     n=read(),++n;
40     split(0),update(rt[1],rt[0]);
41     for(int i=2;i<=n;++i)
42         a[i]=read();
43     for(int i=2,sum=0;i<=n;++i){
44         split(sum^=a[i]),
45         update(rt[i],rt[i-1]),
46         L[i]=query(rt[0],rt[i]);
47     }
48     cnt=0,split(0),update(rt[1],rt[0]); //清零,从后往前再来一遍
49     for(int i=n,sum=0;i>=2;--i){
50         split(sum^=a[i]);
51         update(rt[n-i+2],rt[n-i+1]),
52         R[i]=query(rt[0],rt[n-i+2]);
53     } lef=L[2];
54     for(int i=3;i<=n;++i){ //从左到右处理答案
55         res=max(res,lef+R[i]),
56         lef=max(lef,L[i]);
57     } printf("%d\n",res); return 0;
58 }

原文地址:https://www.cnblogs.com/Judge/p/9533071.html

时间: 2024-10-08 18:51:12

Nikitosh 和异或 —— 一道 trie 树的题用可持久化 trie 水 然后翻车了...的相关文章

hdu 4828 Xor Sum (trie 树模板题,经典应用)

hdu 4825 题目链接 题意:给定n个数,然后给出m个询问,每组询问一个数x,问n中的数y使得x和y的异或和最大. 思路:字典树..把每个数转化成二进制,注意补全前导0,使得所有数都有相同的位数. 如果想要异或和最大,那么每一位尽可能都是1. 所以做法是,先构建字典树,然后每次find的时候,尽可能按照和当前寻找的数的位相反的位的方向走(如果有的话) 比如当前位是1,那我就往0的方向走. 需要注意的是,多组数据,每次要重新初始化一遍. 做法是 在struct 中重新 root = new N

poj3630 Phone List (trie树模板题)

Phone List Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 26328   Accepted: 7938 Description Given a list of phone numbers, determine if it is consistent in the sense that no number is the prefix of another. Let's say the phone catalogu

HDU 1251 Trie树模板题

1.HDU 1251 统计难题  Trie树模板题,或者map 2.总结:用C++过了,G++就爆内存.. 题意:查找给定前缀的单词数量. #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b #define F(i,a,b

【hdoj】1251 统计难题 【数据结构-Trie树裸题】

传送门:统计难题 题意: 字典树裸题. 分析 字典树板子,但是这题需要注意一点. 关于字典树的只是可以参考hihocoder hiho一下 第二周 用G++提交会爆内存(Memory Limit Exceeded),用c++提交可以AC. G++ 与 C++提交的区别 参考:OJ中的语言选项里G++ 与 C++的区别 C++是一门计算机编程语言,而G++则是C++的编译器. 选择C++意味着你将使用C++最标准的编译方式,也就是ANSI C++编译. 选择G++则意味这你使用GNU项目中适用人群

HDU 1671 Phone List (Trie树 好题)

Phone List Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 11721    Accepted Submission(s): 3982 Problem Description Given a list of phone numbers, determine if it is consistent in the sense th

HiHo1014 : Trie树(Trie树模板题)

描述 小Hi和小Ho是一对好朋友,出生在信息化社会的他们对编程产生了莫大的兴趣,他们约定好互相帮助,在编程的学习道路上一同前进. 这一天,他们遇到了一本词典,于是小Hi就向小Ho提出了那个经典的问题:“小Ho,你能不能对于每一个我给出的字符串,都在这个词典里面找到以这个字符串开头的所有单词呢?” 身经百战的小Ho答道:“怎么会不能呢!你每给我一个字符串,我就依次遍历词典里的所有单词,检查你给我的字符串是不是这个单词的前缀不就是了?” 小Hi笑道:“你啊,还是太年轻了!~假设这本词典里有10万个单

835. 字符串统计(Trie树模板题)

维护一个字符串集合,支持两种操作: “I x”向集合中插入一个字符串x: “Q x”询问一个字符串在集合中出现了多少次. 共有N个操作,输入的字符串总长度不超过 105105,字符串仅包含小写英文字母. 输入格式 第一行包含整数N,表示操作数. 接下来N行,每行包含一个操作指令,指令为”I x”或”Q x”中的一种. 输出格式 对于每个询问指令”Q x”,都要输出一个整数作为结果,表示x在集合中出现的次数. 每个结果占一行. 数据范围 1≤N≤2∗1041≤N≤2∗104 输入样例: 5 I a

hihocoder_1014: Trie树(Trie树模板题)

题目链接 #include<bits/stdc++.h> using namespace std; const int L=12; struct T { int num; T* next[26]; T() { num=0; int i; for(int i=0;i<26;i++) next[i]=NULL; } }t; void insert(char str[]) { T* p=&t; for(int i=0;str[i];i++) { int a=str[i]-'a'; if

hdu 1251 统计难题 (Trie树)

本题是trie树模板题,如果不用trie而用map写可以看出trie处理这类问题有明显的时间优势. 在trie树中查找一个关键字的时间和树中包含的结点数无关,而取决于组成关键字的字符数.(对比:二叉查找树的查找时间和树中的结点数有关O(log2n).) 如果要查找的关键字可以分解成字符序列且不是很长,利用trie树查找速度优于二叉查找树. 若关键字长度最大是5,则利用trie树,利用5次比较可以从265=11881376个可能的关键字中检索出指定的关键字.而利用二叉查找树至少要进行log2265