可持久化trie 学习总结
QAQ 以前一直觉得可持久化trie很难,今天强行写了一发觉得还是蛮简单的嘛
自己的模板是自己手写的,写了几道题目并没有出过错误
THUSC的第二题的解法五貌似就是可持久化trie,时间复杂度O(60*n*logn)
不过并没有正解优,听说考场上有人写可持久化树链剖分,也是6得不行QAQ
可持久化trie就是你每次插入一个单词的时候将原来trie的代码每次向下走的时候新建节点
把当前节点信息拷贝给新建节点,通常情况下还要额外对于trie的每个节点维护子树的信息
BZOJ 3261
也算是经典题目了吧,转化成后缀异或和之后变成了选出一个数使得其异或x最大
然后直接在trie上贪心就可以了
至于[L,R]的限制,用可持久化trie维护sz域,然后每次判断的时候减一减看看是不是为0就可以了
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std; int n,m,tot,now,blo;
int L,R,x;
int a[300010];
int rt[600010];
int nxt[16000010][2];
int sz[16000010]; void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void insert(int &o,int v,int dep){
++tot;
nxt[tot][0]=nxt[o][0];
nxt[tot][1]=nxt[o][1];
sz[tot]=sz[o]+1;o=tot;
if(dep==-1)return;
if(v>>dep&1)insert(nxt[o][1],v,dep-1);
else insert(nxt[o][0],v,dep-1);
}
int Get_ans(int a,int b,int v){
int ans=0;
for(int i=24;i>=0;--i){
if(v>>i&1){
int A=nxt[a][0],B=nxt[b][0];
if(sz[B]-sz[A]>0)ans|=(1<<i),a=A,b=B;
else a=nxt[a][1],b=nxt[b][1];
}else{
int A=nxt[a][1],B=nxt[b][1];
if(sz[B]-sz[A]>0)ans|=(1<<i),a=A,b=B;
else a=nxt[a][0],b=nxt[b][0];
}
}
return ans;
}
int main(){
read(n);read(m);
for(int i=1;i<=n;++i)read(a[i]);
for(int i=n-1;i>=1;--i)a[i]^=a[i+1];
rt[0]=1;tot=1;
for(int i=1;i<=n;++i){
rt[i]=rt[i-1];
insert(rt[i],a[i],24);
}blo=n;
while(m--){
char ch=getchar();
while(ch<'!')ch=getchar();
if(ch=='A'){
read(x);rt[blo+1]=rt[blo];blo++;
now^=x;x^=now;
insert(rt[blo],x,24);
}else{
read(L);read(R);
read(x);x^=now;
printf("%d\n",Get_ans(rt[L-1],rt[R],x));
}
}return 0;
}
BZOJ 3166
看完题之后发现只要知道每个点作为次小值的可能区间就转化成了上面的问题
至于求可行区间就是51Nod的 移数博弈 的原题了QAQ
直接用链表搞一搞就搞出来了,查询用可持久化trie就可以了
其实我的写法略微有些蠢
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; const int maxn=50010;
int n,tot=1;
int sub[maxn],pre[maxn];
struct Num{
int v,id;
}t[maxn];
int rt[maxn];
int nxt[2000010][2];
int sz[2000010];
int ans=0;
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
bool cmp(const Num &A,const Num &B){
return A.v<B.v;
}
void insert(int &o,int v,int dep){
++tot;
nxt[tot][0]=nxt[o][0];
nxt[tot][1]=nxt[o][1];
sz[tot]=sz[o]+1;o=tot;
if(dep==-1)return;
if(v>>dep&1)insert(nxt[o][1],v,dep-1);
else insert(nxt[o][0],v,dep-1);
}
int ask(int a,int b,int v){
int ans=0;
for(int i=31;i>=0;--i){
int x=(v>>i&1);
int A=nxt[a][x^1],B=nxt[b][x^1];
if(sz[B]-sz[A]>0)ans|=(1<<i),a=A,b=B;
else a=nxt[a][x],b=nxt[b][x];
}return ans;
}
void del(int now){
pre[sub[now]]=pre[now];
sub[pre[now]]=sub[now];
} int main(){
read(n);
for(int i=1;i<=n;++i)read(t[i].v),t[i].id=i;
rt[0]=1;
for(int i=1;i<=n;++i){
rt[i]=rt[i-1];
insert(rt[i],t[i].v,31);
}
sort(t+1,t+n+1,cmp);
for(int i=1;i<=n;++i)sub[i]=i+1,pre[i]=i-1;
pre[0]=0;sub[n+1]=n+1;sub[0]=1;pre[n+1]=n;
for(int i=1;i<=n;++i){
int now=t[i].id;
int L=pre[now],R=sub[now];
int l=pre[L],r=sub[R];
if(l+1<=R-1)ans=max(ans,ask(rt[l],rt[R-1],t[i].v));
if(L+1<=r-1)ans=max(ans,ask(rt[L],rt[r-1],t[i].v));
del(now);
}printf("%d\n",ans);
return 0;
}
BZOJ 2741
一开始看到题目一脸懵逼,之后看到数据范围就秒懂了做法
显然数据范围这么小,O(30*n*sqrt(n))也不会挂
转化成前缀和之后是很经典的问题
我们直接分块,预处理f[i][j]表示i->j这些块内的答案
每次询问只需要考虑散点就可以了,直接暴力然后在可持久化trie上跑一跑就可以了
我的预处理写的略微麻烦了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
using namespace std; const int maxn=20010;
typedef long long LL;
int n,m,tot,blo,k;
int nxt[1000010][2];
int sz[1000010];
int pos[maxn];
int rt[maxn];
int Ans[210][210];
LL a[maxn];
LL x,y;
LL la; int Get_Node(){
tot++;
nxt[tot][0]=nxt[tot][1]=0;
return tot;
}
int Get_ask(int v){
int ans=0,now=1;
for(int i=31;i>=0;--i){
int x=(v>>i&1);
if(nxt[now][x^1])ans|=(1<<i),now=nxt[now][x^1];
else now=nxt[now][x];
}return ans;
}
void Get_insert(int v){
int now=1;
for(int i=31;i>=0;--i){
int id=(v>>i&1);
if(!nxt[now][id])nxt[now][id]=Get_Node();
now=nxt[now][id];
}return;
}
void insert(int &o,int v,int dep){
++tot;
nxt[tot][0]=nxt[o][0];
nxt[tot][1]=nxt[o][1];
sz[tot]=sz[o]+1;o=tot;
if(dep==-1)return;
if(v>>dep&1)insert(nxt[o][1],v,dep-1);
else insert(nxt[o][0],v,dep-1);
}
int ask(int a,int b,int v){
int ans=0;
for(int i=31;i>=0;--i){
int x=(v>>i&1);
int A=nxt[a][x^1],B=nxt[b][x^1];
if(sz[B]-sz[A]>0)ans|=(1<<i),a=A,b=B;
else a=nxt[a][x],b=nxt[b][x];
}return ans;
}
void Get_ans(int u,int v){
int A=pos[u],B=pos[v];
la=0;
if(A==B){
for(int i=u;i<=v;++i){
int tmp=ask(rt[u-1],rt[i],a[i]);
if(tmp>la)la=tmp;
}return;
}
la=Ans[A+1][B-1];
int lim=(B-1)*blo+1;
for(int i=lim;i<=v;++i){
int tmp=ask(rt[u-1],rt[i],a[i]);
if(tmp>la)la=tmp;
}
lim=A*blo;
for(int i=u;i<=lim;++i){
int tmp=ask(rt[i],rt[v],a[i]);
if(tmp>la)la=tmp;
}return;
}
int main(){
scanf("%d%d",&n,&m);
blo=(int)(sqrt(n));
k=(n%blo==0?n/blo:n/blo+1);
for(int i=1;i<=n;++i)scanf("%lld",&a[i]),a[i]^=a[i-1],pos[i]=(i-1)/blo+1;
for(int i=1;i<=k;++i){
int tmp=0;tot=1;nxt[tot][0]=nxt[tot][1]=0;
for(int j=i;j<=k;++j){
int L=(j-1)*blo+1,R=min(n,j*blo);
for(int k=L;k<=R;++k){
Get_insert(a[k]);
tmp=max(tmp,Get_ask(a[k]));
}Ans[i][j]=tmp;
}
}
rt[0]=1;tot=1;nxt[tot][0]=nxt[tot][1]=0;
for(int i=1;i<=n;++i){
rt[i]=rt[i-1];
insert(rt[i],a[i],31);
}
while(m--){
scanf("%lld%lld",&x,&y);
x=(x+la)%n+1;y=(y+la)%n+1;
if(x>y)swap(x,y);
x--;Get_ans(x,y);
printf("%lld\n",la);
}return 0;
}
BZOJ 4260
这么丝薄的题目。。QAQ
想起一道POJ的题目,貌似是求两段不相交的子序列使得总和最大
做法是正着DP一遍,倒着DP一遍,然后枚举分界点
这道题同理,我们正着扫一遍,一边扫一边建trie并维护答案
之后再倒着扫一遍,然后统计答案即可
不过这道题目貌似跟可持久化trie没什么关系..
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std; typedef long long LL;
const int maxn=400010;
int n,tot;
int a[maxn],sum[maxn];
int pre[maxn],sub[maxn];
int nxt[13000010][2];
LL ans; void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
int Get_Node(){
tot++;
nxt[tot][0]=nxt[tot][1]=0;
return tot;
}
int ask(int v){
int ans=0,now=1;
for(int i=31;i>=0;--i){
int x=(v>>i&1);
if(!nxt[now][x^1])now=nxt[now][x];
else now=nxt[now][x^1],ans|=(1<<i);
}return ans;
}
void insert(int v){
int now=1;
for(int i=31;i>=0;--i){
int id=(v>>i&1);
if(!nxt[now][id])nxt[now][id]=Get_Node();
now=nxt[now][id];
}return;
} int main(){
read(n);
for(int i=1;i<=n;++i)read(a[i]);
for(int i=1;i<=n;++i)sum[i]=(a[i]^sum[i-1]);
tot=1;nxt[tot][0]=nxt[tot][1]=0;
insert(sum[0]);
for(int i=1;i<=n;++i){
pre[i]=max(ask(sum[i]),pre[i-1]);
insert(sum[i]);
}
tot=1;nxt[tot][0]=nxt[tot][1]=0;
insert(sum[n]);
for(int i=n-1;i>=0;--i){
sub[i]=max(ask(sum[i]),sub[i-1]);
insert(sum[i]);
}
for(int i=1;i<=n-1;++i){
LL tmp=sub[i]+pre[i];
if(tmp>ans)ans=tmp;
}printf("%lld\n",ans);
return 0;
}
BZOJ 4546
赤裸裸的可持久化trie的模板题啊
由于可持久化所以1,3操作随便写
最大异或和在trie上跑一跑就可以了
至于查询第k大和查询某个数的排名的话,直接在trie上跑,模拟一下就可以了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
using namespace std; const int maxn=500010;
int n,type;
int L,R,x,cnt,tot=1;
int rt[maxn];
int nxt[10500010][2];
int sz[10500010]; void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void insert(int &o,int v,int dep){
++tot;
nxt[tot][0]=nxt[o][0];
nxt[tot][1]=nxt[o][1];
sz[tot]=sz[o]+1;o=tot;
if(dep==-1)return;
if(v>>dep&1)insert(nxt[o][1],v,dep-1);
else insert(nxt[o][0],v,dep-1);
}
int ask(int a,int b,int v){
int ans=0,now=1;
for(int i=19;i>=0;--i){
int x=(v>>i&1);
int A=nxt[a][x^1],B=nxt[b][x^1];
if(sz[B]-sz[A]>0)ans|=(1<<i),a=A,b=B;
else a=nxt[a][x],b=nxt[b][x];
}return ans;
}
int Get_ans(int a,int b,int v){
int ans=0;
for(int i=19;i>=0;--i){
int x=(v>>i&1);
if(x)ans+=sz[nxt[b][0]]-sz[nxt[a][0]];
a=nxt[a][x];b=nxt[b][x];
}ans+=sz[b]-sz[a];
return ans;
}
int Get_Num(int a,int b,int k){
int ans=0;
for(int i=19;i>=0;--i){
int tmp=sz[nxt[b][0]]-sz[nxt[a][0]];
if(tmp>=k)a=nxt[a][0],b=nxt[b][0];
else ans|=(1<<i),a=nxt[a][1],b=nxt[b][1],k-=tmp;
}return ans;
}
int main(){
read(n);rt[0]=1;
while(n--){
read(type);
if(type==1){
read(x);rt[cnt+1]=rt[cnt];cnt++;
insert(rt[cnt],x,19);
}else if(type==2){
read(L);read(R);read(x);
printf("%d\n",ask(rt[L-1],rt[R],x)^x);
}else if(type==3){
read(x);cnt=cnt-x;
}else if(type==4){
read(L);read(R);read(x);
printf("%d\n",Get_ans(rt[L-1],rt[R],x));
}else{
read(L);read(R);read(x);
printf("%d\n",Get_Num(rt[L-1],rt[R],x));
}
}return 0;
}
hdu 4757
查询树上u-v的链上的数和w的最大异或和
经典的可持久化数据结构上树
一样的写法,在树上DFS一下就可以了
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; const int maxn=100010;
int n,m,u,v,w,tot,cnt;
int a[maxn],rt[maxn];
int h[maxn],dep[maxn];
int anc[maxn][20];
int nxt[2000010][2];
int sz[2000010];
struct edge{
int to,next;
}G[maxn<<1]; void add(int x,int y){
++cnt;G[cnt].to=y;G[cnt].next=h[x];h[x]=cnt;
}
void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
void insert(int &o,int v,int dep){
++tot;
nxt[tot][1]=nxt[o][1];
nxt[tot][0]=nxt[o][0];
sz[tot]=sz[o]+1;o=tot;
if(dep==-1)return;
if(v>>dep&1)insert(nxt[o][1],v,dep-1);
else insert(nxt[o][0],v,dep-1);
}
int ask(int a,int b,int c,int v,int tmp){
int ans=0;
for(int i=15;i>=0;--i){
int x=(v>>i&1);
int now=sz[nxt[a][x^1]]+sz[nxt[b][x^1]]-2*sz[nxt[c][x^1]];
if(now>0)ans|=(1<<i),a=nxt[a][x^1],b=nxt[b][x^1],c=nxt[c][x^1];
else a=nxt[a][x],b=nxt[b][x],c=nxt[c][x];
}return max(ans,v^tmp);
}
void DFS(int u,int f){
anc[u][0]=f;
rt[u]=rt[f];insert(rt[u],a[u],15);
for(int i=h[u];i;i=G[i].next){
int v=G[i].to;
if(v==f)continue;
dep[v]=dep[u]+1;
DFS(v,u);
}return;
}
void pre_LCA(){
anc[1][0]=-1;
for(int i=1;i<=n;++i){
for(int j=1;(1<<j)<=n;++j)anc[i][j]=-1;
}
for(int j=1;(1<<j)<=n;++j){
for(int i=1;i<=n;++i){
if(anc[i][j-1]!=-1){
int a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
}
}
}return;
}
int LCA(int p,int q){
if(dep[p]<dep[q])swap(p,q);
int log;
for(log=0;(1<<log)<=dep[p];++log);--log;
for(int i=log;i>=0;--i){
if(dep[p]-(1<<i)>=dep[q])p=anc[p][i];
}
if(p==q)return p;
for(int i=log;i>=0;--i){
if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){
p=anc[p][i];q=anc[q][i];
}
}return anc[p][0];
} int main(){
while(scanf("%d%d",&n,&m)==2){
for(int i=1;i<=n;++i)read(a[i]);
memset(h,0,sizeof(h));cnt=0;
for(int i=1;i<n;++i){
read(u);read(v);
add(u,v);add(v,u);
}
tot=1;nxt[tot][0]=nxt[tot][1]=0;
rt[0]=1;DFS(1,0);pre_LCA();
while(m--){
read(u);read(v);read(w);
int lca=LCA(u,v);
printf("%d\n",ask(rt[u],rt[v],rt[lca],w,a[lca]));
}
}return 0;
}
可持久化trie总结:
1、多半情况下是二进制trie,用来解决异或和问题
一般用来解决区间异或和,因为子集异或和我们有萌萌哒线性基QAQ
2、内存很容易爆炸,所以在写之前要算内存并且看内存限制
其实可持久化trie很无脑QAQ看出来麻麻麻就可以了
可持久化trie 学习总结的更多相关文章
- 可持久化trie学习笔记
其实很早之前就想学习可持久化trie,不过由于换队友等情况,还是优先去学数论和计算几何,今天突然心血来潮学了一发可持久化trie,感觉还是蛮简单的,不过由于自己很长时间没写过可持久化了,都快忘了是个什 ...
- [BZOJ3261&BZOJ3166]可持久化trie树及其应用
可持久化trie树 可持久化trie树现在想来是比较好理解的了,但却看了一个下午... 相当于对于每个状态建立一条链(或者说一棵trie),求解的时候只要让两个点按照相同的步子走然后看sum的大小关系 ...
- [FJOI2015]火星商店问题(线段树分治,可持久化,Trie树)
[FJOI2015]火星商店问题 前天考了到线段树分治模板题,全场都切了,就我不会QAQ 于是切题无数的Tyher巨巨就告诉我:"你可以去看看火星商店问题,看了你就会了." 第一道 ...
- 可持久化Trie模板
如果你了解过 01 Trie 和 可持久化线段树(例如 : 主席树 ).那么就比较好去可持久化 Trie 可持久化 Trie 当 01 Trie 用的时候能很方便解决一些原本 01 Trie 不能解决 ...
- Trie学习总结
Trie树学习总结 字典树,又称前缀树,是用于快速处理字符串的问题,能做到快速查找到一些字符串上的信息. 另外,Trie树在实现高效的同时,会损耗更多的空间,所以Trie是一种以空间换时间的算法. T ...
- 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))得到的并不直接是 ...
- 可持久化Trie & 可持久化平衡树 专题练习
[xsy1629]可持久化序列 - 可持久化平衡树 http://www.cnblogs.com/Sdchr/p/6258827.html [bzoj4260]REBXOR - Trie 事实上只是一 ...
- HDU 4757 Tree(可持久化trie)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4757 题意:给出一棵树,节点有权值.每次询问x到y的路径上与z抑或的最大值. 思路:可持久化trie. ...
随机推荐
- lnmp下配置虚拟主机
一:首先熟悉几个命令 which php ---> which是通过 PATH环境变量到该路径内查找可执行文件,所以基本的功能是寻找可执行文件 whereis php ----& ...
- Linux C 程序 GTK+图形界面编程(22)
GTK+图形界面编程 Linux大多是在字符界面,但也可以开发图形界面 目前已经存在多种Linux下开发图形界面的程序开发包:最常用的是Qt和GTK+ Qt是一个跨平台的图形界面开发库,不仅仅支持Li ...
- 如何开启MYSQL远程连接权限
开启MYSQL远程连接权限 //建议设置固定IP mysql> GRANT ALL PRIVILEGES ON *.* TO root@"8.8.8.8" IDENTIFIE ...
- FineUI 框架,RIA 富客户端应用的选择
FineUI 框架演示地址:http://www.fineui.com/demo/ 是asp.net 和extjs 结合的框架,可以快速创建企业应用程序的界面,节省开发时间,具体使用详见fineUI ...
- Mysql主从同步(复制)
目录: mysql主从同步定义 主从同步机制 配置主从同步 配置主服务器 配置从服务器 使用主从同步来备份 使用mysqldump来备份 备份原始文件 ...
- Ubuntu下Code::Blocks无法编译 /bin/sh: 1: g++ not found 解决办法
Linux下Code::Blocks无法编译运行提示 /bin/sh: 1: g++ not found 的解决办法 今天在Ubuntu 12.04 软件中心中选装了Code::Blocks,安装完成 ...
- c++构造函数详解
c++构造函数的知识在各种c++教材上已有介绍,不过初学者往往不太注意观察和总结其中各种构造函数的特点和用法,故在此我根据自己的c++编程经验总结了一下c++中各种构造函数的特点,并附上例子,希望对初 ...
- phonegap ios插件开发及无限后台运行解决
1.首先开发插件:因为我的项目前需要所以要做(根据情况) 在项目的plugins文件中新建obj c文件.如 Demo,此时会产生出Demo.h和Demo.m两个文件. .h文件主要就是定义一些方法, ...
- SNV ConnerStore使用说明
1. 上传自己新建的文件新建的类文件 后面的 会有A标示要先 Add To Working copy 再点击提交 2. 上传第三方库时 默认SVN是忽略.a文件的要找到.a文件把他设置成不是忽略的通 ...
- sharepoint 认证
MCPD http://www.microsoft.com/learning/en/us/mcpd-certification.aspx#fbid=YktyKIYXeFg Exam 70-573: T ...