可持久化Trie & 可持久化平衡树 专题练习
【xsy1629】可持久化序列 - 可持久化平衡树
http://www.cnblogs.com/Sdchr/p/6258827.html
【bzoj4260】REBXOR - Trie
事实上只是一道Trie树的题。
只是被林导钦定成可持久化的了。
区间异或和,转化为前缀异或和。
预处理向前最大的异或和,向后最大的异或和,和前缀最大异或和,枚举分割点求答案。
#include <cstdio>
#include <cctype>
#include <climits>
#include <algorithm>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define per(i,a,b) for (int i=(a);i>=(b);i--)
const int N=524288;
const int B=30;
const int S=16777216;
const int MIN=INT_MIN;
int n;
int a[N];
int pre[N],tot_Pre[N];
int suf[N];
int res;
namespace Trie {
int rt,tot;
struct Node {
int c[2];
Node(void) {
c[0]=c[1]=0;
}
}tr[S];
void Init(void) {
rep(i,1,tot) tr[i]=Node();
rt=tot=1;
}
void Ins(int &x,int num,int bit) {
if (!x) x=++tot;
if (bit==-1) return;
Ins(tr[x].c[num>>bit&1],num,bit-1);
}
int Query_Max(int x,int num,int bit) {
if (bit==-1) return 0;
int dir=((num>>bit&1)^1);
if (tr[x].c[dir]>0) {
int t=Query_Max(tr[x].c[dir],num,bit-1);
return t+(1<<bit);
}
else {
int t=Query_Max(tr[x].c[dir^1],num,bit-1);
return t;
}
}
}
using namespace Trie;
int rd(void) {
int x=0,f=1; char c=getchar();
while (!isdigit(c)) {
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int main(void) {
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=rd();
rep(i,1,n) a[i]=rd();
rep(i,1,n) a[i]^=a[i-1];
Init();
rep(i,1,n) {
Ins(rt,a[i-1],B);
pre[i]=Query_Max(rt,a[i],B);
}
tot_Pre[1]=pre[1];
rep(i,2,n)
tot_Pre[i]=max(tot_Pre[i-1],pre[i]);
Init();
per(i,n,1) {
Ins(rt,a[i],B);
suf[i]=Query_Max(rt,a[i-1],B);
}
res=MIN;
per(i,n,2) {
int t=tot_Pre[i-1]+suf[i];
res=max(res,t);
}
printf("%d\n",res);
return 0;
}
【bzoj3439】Kpm的MC密码 - Trie + Treap启发式合并
传送门
http://www.lydsy.com/JudgeOnline/problem.php?id=3439
分析
把串反过来,那么相当于问每一个字符串的后缀中编号第\(k\)小的。
考虑建一棵Trie,那么就是询问某个子树内编号的第\(k\)小。
方法1:
转化为dfs序,用可持久化线段树来求解。
或者离线后乱搞。
方法2:
把Trie进行DFS,启发式合并。
代码
#include <cstdio>
#include <cstring>
#include <ctime>
#include <cctype>
#include <algorithm>
#include <vector>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
#define VI vector<int>
#define pb push_back
const int L=524288;
const int A=26;
const int N=131072;
int n;
char s[L];
int rt,tot;
struct Trie {
int c[A];
VI pt;
}tr[L];
int k[N];
int ans[N];
int root[L]; //int tot;
struct Treap {
int c[2];
int key,fix;
int siz;
Treap(int _key=0,int _siz=0) {
c[0]=c[1]=0;
key=_key,fix=rand();
siz=_siz;
}
}trp[L];
struct D {
int c[2];
D(void) {
c[0]=c[1]=0;
}
};
void Ins_Trie(int &x,char *s,int len,int id) {
if (!x) x=++tot;
if (!len) {
tr[x].pt.pb(id);
return;
}
Ins_Trie(tr[x].c[s[len]-'a'],s,len-1,id);
}
int New_Node(int key) {
int x=++tot;
trp[x]=Treap(key,1);
return x;
}
void Update(int x) {
trp[x].siz=trp[trp[x].c[0]].siz+trp[trp[x].c[1]].siz+1;
}
int Merge(int x1,int x2) {
if (!x1) return x2;
if (!x2) return x1;
if (trp[x1].fix<trp[x2].fix) {
trp[x1].c[1]=Merge(trp[x1].c[1],x2);
Update(x1);
return x1;
}
else {
trp[x2].c[0]=Merge(x1,trp[x2].c[0]);
Update(x2);
return x2;
}
}
D Split_Key(int x,int key) {
D t; if (!x) return t;
if (key<trp[x].key) {
t=Split_Key(trp[x].c[0],key);
trp[x].c[0]=t.c[1]; Update(x);
t.c[1]=x;
}
else {
t=Split_Key(trp[x].c[1],key);
trp[x].c[1]=t.c[0]; Update(x);
t.c[0]=x;
}
return t;
}
D Split(int x,int rk) {
D t; if (!x) return t;
if (rk<=trp[trp[x].c[0]].siz) {
t=Split(trp[x].c[0],rk);
trp[x].c[0]=t.c[1]; Update(x);
t.c[1]=x;
}
else if (rk==trp[trp[x].c[0]].siz+1) {
t.c[1]=trp[x].c[1];
trp[x].c[1]=0; Update(x);
t.c[0]=x;
}
else {
t=Split(trp[x].c[1],rk-1-trp[trp[x].c[0]].siz);
trp[x].c[1]=t.c[0]; Update(x);
t.c[0]=x;
}
return t;
}
void Add_Point(int &to,int x) {
D t=Split_Key(to,trp[x].key);
to=Merge(t.c[0],x); to=Merge(to,t.c[1]);
}
void Add_Tree(int &to,int x) {
if (!x) return;
Add_Tree(to,trp[x].c[0]);
Add_Tree(to,trp[x].c[1]);
trp[x].c[0]=trp[x].c[1]=0; Update(x);
Add_Point(to,x);
}
int Join(int x1,int x2) {
if (trp[x1].siz<trp[x2].siz) swap(x1,x2);
Add_Tree(x1,x2);
return x1;
}
int Query_Kth(int x,int rk) {
int siz=trp[x].siz;
if (!(1<=rk&&rk<=siz)) return -1;
D t1=Split(x,rk-1);
D t2=Split(t1.c[1],1);
int g=trp[t2.c[0]].key;
x=Merge(t1.c[0],t2.c[0]); x=Merge(x,t2.c[1]);
return g;
}
void DFS(int x) {
rep(i,0,A-1)
if (tr[x].c[i]>0)
DFS(tr[x].c[i]);
rep(i,0,A-1)
if (tr[x].c[i]>0)
root[x]=Join(root[x],root[tr[x].c[i]]);
rep(i,1,tr[x].pt.size()) {
int p=New_Node(tr[x].pt[i-1]);
Add_Point(root[x],p);
}
rep(i,1,tr[x].pt.size()) {
int p=tr[x].pt[i-1];
ans[p]=Query_Kth(root[x],k[p]);
}
}
int rd(void) {
int x=0,f=1; char c=getchar();
while (!isdigit(c)) {
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int main(void) {
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
srand(time(0));
n=rd(); rt=tot=1;
rep(i,1,n) {
scanf("%s",s+1);
Ins_Trie(rt,s,strlen(s+1),i);
}
rep(i,1,n) k[i]=rd();
DFS(rt);
rep(i,1,n)
printf("%d\n",ans[i]);
return 0;
}
小结
(1)对于一棵树上的子树的离线询问,可以考虑使用启发式合并来求解。
(2)树上询问的处理技巧:
- dfs序的性质,虚树
- 树链剖分
- LCT
- 树上倍增,树上倍增dp
- 树上莫队
- 点分治
- 离线dfs,启发式合并
【bzoj3261】最大异或和 - 可持久化Trie
代码
#include <cstdio>
#include <cctype>
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
const int LEN=1048576;
const int S=16777216;
const int BIT=23;
int n,a[LEN];
int m;
int tot,rt[LEN],tms;
struct Trie {
int c[2],cnt;
Trie(void) {
c[0]=c[1]=cnt=0;
}
}tr[S];
int rd(void) {
int x=0,f=1; char c=getchar();
while (!isdigit(c)) {
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int Ins(int x,int bit,int num) {
int now=++tot; tr[now]=tr[x]; tr[now].cnt++;
if (bit==-1) return now;
tr[now].c[num>>bit&1]=Ins(tr[x].c[num>>bit&1],bit-1,num);
return now;
}
int Query(int x1,int x2,int bit,int num) {
if (bit==-1) return 0;
int prefer=((num>>bit&1)^1);
int cnt=tr[tr[x2].c[prefer]].cnt-tr[tr[x1].c[prefer]].cnt;
if (cnt>0) {
int t=Query(tr[x1].c[prefer],tr[x2].c[prefer],bit-1,num);
return t+(1<<bit);
}
else {
int t=Query(tr[x1].c[prefer^1],tr[x2].c[prefer^1],bit-1,num);
return t;
}
}
int main(void) {
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
n=rd(),m=rd();
rep(i,1,n) a[i]=rd();
rep(i,2,n) a[i]^=a[i-1];
rep(i,0,n)
rt[i]=Ins(rt[i-1],BIT,a[i]);
tms=0;
rep(i,1,m) {
scanf("\n"); char c=getchar();
if (c=='A') {
int x=rd(); a[n+1]=(x^a[n]);
rt[n+1]=Ins(rt[n],BIT,a[n+1]);
n++;
}
else {
int l=rd(),r=rd(),x=rd();
int ans=Query(rt[l-2>=0?l-2:n+1],rt[r-1],BIT,a[n]^x);
printf("%d\n",ans);
}
}
return 0;
}
【bzoj3166】ALO - Treap+可持久化Trie树
枚举次大值的坐标,那么可以通过前驱、后继得到一段区间。
#include <cstdio>
#include <cctype>
#include <ctime>
#include <algorithm>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<=(b);i++)
const int N=65536;
const int S=2097152;
const int BIT=30;
int n;
int a[N];
int tot,rt[N];
struct Trie {
int c[2]; int cnt;
Trie(void) {
c[0]=c[1]=cnt=0;
}
}tr[S];
int ord[N]; int mx;
int root;
struct Treap {
int c[2];
int key,fix;
int siz;
Treap(int _key=0,int _siz=0) {
c[0]=c[1]=0;
key=_key,fix=rand();
siz=_siz;
}
}p[N];
struct D {
int c[2];
D(void) {
c[0]=c[1]=0;
}
};
int ans;
int rd(void) {
int x=0,f=1; char c=getchar();
while (!isdigit(c)) {
if (c=='-') f=-1;
c=getchar();
}
while (isdigit(c)) {
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int Ins_Trie(int x,int bit,int num) {
int now=++tot; tr[now]=tr[x]; tr[now].cnt++;
if (bit==-1) return now;
tr[now].c[num>>bit&1]=Ins_Trie(tr[x].c[num>>bit&1],bit-1,num);
return now;
}
int Query_Trie(int x1,int x2,int bit,int num) {
if (bit==-1) return 0;
int prefer=((num>>bit&1)^1);
int cnt=tr[tr[x2].c[prefer]].cnt-tr[tr[x1].c[prefer]].cnt;
if (cnt>0) {
int t=Query_Trie(tr[x1].c[prefer],tr[x2].c[prefer],bit-1,num);
return t+(1<<bit);
}
else {
int t=Query_Trie(tr[x1].c[prefer^1],tr[x2].c[prefer^1],bit-1,num);
return t;
}
}
int New_Node(int key) {
int x=++tot;
p[x]=Treap(key,1);
return x;
}
void Update(int x) {
p[x].siz=p[p[x].c[0]].siz+p[p[x].c[1]].siz+1;
}
int Merge(int x1,int x2) {
if (!x1) return x2;
if (!x2) return x1;
if (p[x1].fix<p[x2].fix) {
p[x1].c[1]=Merge(p[x1].c[1],x2);
Update(x1);
return x1;
}
else {
p[x2].c[0]=Merge(x1,p[x2].c[0]);
Update(x2);
return x2;
}
}
D Split_Key(int x,int key) {
D t; if (!x) return t;
if (key<p[x].key) {
t=Split_Key(p[x].c[0],key);
p[x].c[0]=t.c[1]; Update(x);
t.c[1]=x;
}
else {
t=Split_Key(p[x].c[1],key);
p[x].c[1]=t.c[0]; Update(x);
t.c[0]=x;
}
return t;
}
D Split(int x,int rk) {
D t; if (!x) return t;
if (rk<=p[p[x].c[0]].siz) {
t=Split(p[x].c[0],rk);
p[x].c[0]=t.c[1]; Update(x);
t.c[1]=x;
}
else if (rk==p[p[x].c[0]].siz+1) {
t.c[1]=p[x].c[1];
p[x].c[1]=0; Update(x);
t.c[0]=x;
}
else {
t=Split(p[x].c[1],rk-1-p[p[x].c[0]].siz);
p[x].c[1]=t.c[0]; Update(x);
t.c[0]=x;
}
return t;
}
int Pre_Pre(int x) {
D t1=Split_Key(root,x);
int siz=p[t1.c[0]].siz,id;
if (siz==1) {
id=-1;
root=Merge(t1.c[0],t1.c[1]);
}
else if (siz==2) {
id=0;
root=Merge(t1.c[0],t1.c[1]);
}
else {
D t2=Split(t1.c[0],siz-3);
D t3=Split(t2.c[1],1);
id=p[t3.c[0]].key;
root=Merge(t3.c[0],t3.c[1]);
root=Merge(t2.c[0],root);
root=Merge(root,t1.c[1]);
}
return id;
}
int Nxt_Nxt(int x) {
D t1=Split_Key(root,x-1);
int siz=p[t1.c[1]].siz,id;
if (siz==1) {
id=-1;
root=Merge(t1.c[0],t1.c[1]);
}
else if (siz==2) {
id=n+1;
root=Merge(t1.c[0],t1.c[1]);
}
else {
D t2=Split(t1.c[1],2);
D t3=Split(t2.c[1],1);
id=p[t3.c[0]].key;
root=Merge(t3.c[0],t3.c[1]);
root=Merge(t2.c[0],root);
root=Merge(t1.c[0],root);
}
return id;
}
void Insert(int &rt,int key) {
int x=New_Node(key);
D t=Split_Key(rt,key);
rt=Merge(t.c[0],x); rt=Merge(rt,t.c[1]);
}
int cmp(int x,int y) {
return a[x]>a[y];
}
int main(void) {
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
srand(2016+02+20);
n=rd();
rep(i,1,n) a[i]=rd();
mx=*max_element(a+1,a+n+1);
rep(i,1,n)
rt[i]=Ins_Trie(rt[i-1],BIT,a[i]);
rep(i,1,n) ord[i]=i;
sort(ord+1,ord+n+1,cmp);
tot=0;
rep(i,1,n) {
int loc=ord[i];
Insert(root,loc);
int x=Nxt_Nxt(loc);
if (!(x==-1&&mx==a[loc])) {
if (x==-1) x=n+1;
if (loc+1<=x-1) {
int t=Query_Trie(rt[loc],rt[x-1],BIT,a[loc]);
ans=max(ans,t);
}
}
x=Pre_Pre(loc);
if (!(x==-1&&mx==a[loc])) {
if (x==-1) x=0;
if (x+1<=loc-1) {
int t=Query_Trie(rt[x],rt[loc-1],BIT,a[loc]);
ans=max(ans,t);
}
}
}
printf("%d\n",ans);
return 0;
}
可持久化Trie & 可持久化平衡树 专题练习的更多相关文章
- 可持久化Trie模板
如果你了解过 01 Trie 和 可持久化线段树(例如 : 主席树 ).那么就比较好去可持久化 Trie 可持久化 Trie 当 01 Trie 用的时候能很方便解决一些原本 01 Trie 不能解决 ...
- [您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解
这一次,我们来了解普通Trie树的变种:0-1Trie以及在其基础上产生的可持久化Trie(其实,普通的Trie也可以可持久化,只是不太常见) 先简单介绍一下0-1Trie:一个0-1Trie节点只有 ...
- HDU 4757 Tree(可持久化Trie+Tarjan离线LCA)
Tree Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 102400/102400 K (Java/Others) Total Su ...
- 【BZOJ4260】 Codechef REBXOR 可持久化Trie
看到异或就去想前缀和(⊙o⊙) 这个就是正反做一遍最大异或和更新答案 最大异或就是很经典的可持久化Trie,从高到低贪心 WA: val&(1<<(base-1))得到的并不直接是 ...
- HDU 4757 Tree(可持久化trie)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4757 题意:给出一棵树,节点有权值.每次询问x到y的路径上与z抑或的最大值. 思路:可持久化trie. ...
- 可持久化trie 学习总结
QAQ 以前一直觉得可持久化trie很难,今天强行写了一发觉得还是蛮简单的嘛 自己的模板是自己手写的,写了几道题目并没有出过错误 THUSC的第二题的解法五貌似就是可持久化trie,时间复杂度O(60 ...
- [BZOJ 4103] [Thu Summer Camp 2015] 异或运算 【可持久化Trie】
题目链接:BZOJ - 4103 题目分析 THUSC滚粗之后一直没有写这道题,从来没写过可持久化Trie,发现其实和可持久化线段树都是一样的.嗯,有些东西就是明白得太晚. 首先Orz ZYF-ZYF ...
- bzoj 2741: 【FOTILE模拟赛】L 分塊+可持久化trie
2741: [FOTILE模拟赛]L Time Limit: 15 Sec Memory Limit: 162 MBSubmit: 1116 Solved: 292[Submit][Status] ...
- bzoj 2741 分块+可持久化trie
多个询问l,r,求所有子区间异或和中最大是多少 强制在线 做法: 分块+可持久化trie 1.对于每块的左端点i,预处理出i到任意一个j,()i,j)间所有子区间异或和中最大为多少,复杂度O(\(n\ ...
随机推荐
- Web自定义协议,BS端启动CS端,
实例 1.准备CS项目,windows窗体应用程序,拖进来一个label控件来接受BS的参数,并显示,右击生成,复制该文件的bin目录下的exe,例如放在以下路径,例如C:\\simu\\下, 2.编 ...
- BZOJ 3551 Peaks加强版
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3551 题意:给出一个图,n个点,m条边.边有权值点也有权值.若干询问,(v,x,k),问从 ...
- 【转载】OGRE 内存管理
原文:OGRE 内存管理 Ogre引擎中与内存管理相关的文件大致有以下几个(只列出头文件) OgreAlignedAllocator.h OgreMemoryAllocatedObject.h Ogr ...
- [POJ1830]开关问题(高斯消元,异或方程组)
题目链接:http://poj.org/problem?id=1830 题意:中文题面,求的是方案数. 首先可以知道, 如果方案数不止一个的话,说明矩阵行列式值为0,即存在自由变元,由于变量只有两种状 ...
- firefox渗透师必备的利器
工欲善必先利其器,firefox一直是各位渗透师必备的利器,小编这里推荐34款firefox渗透测试辅助插件,其中包含渗透测试.信息收集.代理.加密解密等功能. 1:Firebug Firefox的 ...
- EF实体框架常见问题
1,无法为具有固定名称“System.Data.SqlClient”的 ADO.NET 提供程序加载在应用程序配置文件中注册的实体框架提供程序类型“System.Data.Entity.SqlServ ...
- ServiceStack.OrmLite 笔记10-group having 分页orderby等
group having 分页等 var ev = OrmLiteConfig.DialectProvider.SqlExpression(); group的使用 同sql一样,注意group分组的字 ...
- JVM 1.类的加载、连接、初始化
Java类的加载是由类加载器来完成的,过程如下: 首先,加载是把硬盘.网络.数据库等的class文件中的二进制数据加载到内存的过程,然后会在Java虚拟机的运行时数据区的堆区创建一个Class对象,用 ...
- C# 线程(六):定时器
From : http://kb.cnblogs.com/page/42532/ Timer类:设置一个定时器,定时执行用户指定的函数. 定时器启动后,系统将自动建立一个新的线程,执行用户指定的函数. ...
- 关于js运动的一些总结
js运动实现,有两种.一种是速度版,另一种是时间版. 速度版是通过对速度的加减乘除,得出元素的运动数据.时间版是通过对时间进行Tween公式运算,得出元素的运动数据. 速度版运动优点:容易在运动过程中 ...