trie树
题:http://hihocoder.com/problemset/problem/1014
#include<bits/stdc++.h> using namespace std; #define pb push_back typedef long long ll; const int M=1e6+5; int g[M][30],sz[M]; char s[M]; void dfs(int u){ for(int i=0;i<26;i++) if(g[u][i]){ dfs(g[u][i]); sz[u]+=sz[g[u][i]]; } } int main(){ int n; scanf("%d",&n); int tot=1; for(int i=1;i<=n;i++){ scanf("%s",s); int len=strlen(s); int fir=1; for(int j=0;j<len;j++){ if(!g[fir][s[j]-'a']) g[fir][s[j]-'a']=++tot; fir=g[fir][s[j]-'a']; } sz[fir]++; } dfs(1); int m; scanf("%d",&m); while(m--){ scanf("%s",&s); int fir=1; int len=strlen(s); int ans=0; for(int j=0;j<len;j++){ if(!g[fir][s[j]-'a']){ ans=0; break; } fir=g[fir][s[j]-'a']; ans=sz[fir]; } printf("%d\n",ans); } return 0; }
01字典树:
经典问题:n个数,m个查询,每次查询给一个数,找出在n个树中与m异或最大的数
题:http://acm.hdu.edu.cn/showproblem.php?pid=4825
分析:如果是求异或最大的就优先寻找和当前位不同的数,求最小的就优先寻找和当前位相同的数;
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const ll INF=1e18; const int M=1e5+5; const int maxn=26; #define pb push_back #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r struct TRIE{ int val[M*32]; int trie[M*32][2]; int tot,root; void init(){ tot=0; root=0; memset(trie,0,sizeof(trie)); } void insert(int x){ int now=root; for(int i=31;i>=0;i--){ int id=(x>>i)&1; if(!trie[now][id]) trie[now][id]=++tot; now=trie[now][id]; } val[now]=x; } int query(int x){ int now=root; for(int i=31;i>=0;i--){ int id=(x>>i)&1; if(trie[now][id^1]) now=trie[now][id^1]; else now=trie[now][id]; } return val[now]; } }Trie; int main(){ int t; scanf("%d",&t); for(int p=1;p<=t;p++){ int n,m; scanf("%d%d",&n,&m); Trie.init(); for(int i=1;i<=n;i++){ int x; scanf("%d",&x); Trie.insert(x); } printf("Case #%d:\n",p); for(int i=1;i<=m;i++){ int x; scanf("%d",&x); printf("%d\n",Trie.query(x)); } } return 0; }
最长异或路径:树上异或最大路径是多少
题:https://www.luogu.com.cn/problem/P4551
分析:俩点之间的异或等于这俩点分别到根节点的异或和的异或值;
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int inf=0x3f3f3f3f; const ll INF=1e18; const int M=1e5+5; const int maxn=26; #define pb push_back #define lson root<<1,l,midd #define rson root<<1|1,midd+1,r struct TRIE{ int val[M*32]; int trie[M*32][2]; int tot,root; void init(){ tot=0; root=0; memset(trie,0,sizeof(trie)); } void insert(int x){ int now=root; for(int i=31;i>=0;i--){ int id=(x>>i)&1; if(!trie[now][id]) trie[now][id]=++tot; now=trie[now][id]; } val[now]=x; } int query(int x){ int now=root; for(int i=31;i>=0;i--){ int id=(x>>i)&1; if(trie[now][id^1]) now=trie[now][id^1]; else now=trie[now][id]; } return val[now]; } }Trie; int sum[M]; int head[M],tot,ans; struct node{ int v,w,nextt; }e[M<<1]; void addedge(int u,int v,int w){ e[tot].v=v; e[tot].w=w; e[tot].nextt=head[u]; head[u]=tot++; } void dfs(int u,int fa){ ans=max(ans,sum[u]); for(int i=head[u];~i;i=e[i].nextt){ int v=e[i].v; if(v!=fa){ sum[v]=sum[u]^e[i].w; dfs(v,u); } } } int main(){ int n; scanf("%d",&n); memset(head,-1,sizeof(head)); for(int u,v,w,i=1;i<n;i++){ scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); } dfs(1,0); Trie.init(); for(int i=1;i<=n;i++) Trie.insert(sum[i]); for(int i=1;i<=n;i++){ ans=max(ans,sum[i]^Trie.query(sum[i])); } printf("%d\n",ans); return 0; }
题:https://codeforces.com/contest/706/problem/D
题意:支持插入和删除操作,问与询问x异或和最大的数
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<string> #include<vector> #include <ctime> #include<queue> #include<set> #include<map> #include<list> #include<stack> #include<iomanip> #include<cmath> #include<bitset> #define mst(ss,b) memset((ss),(b),sizeof(ss)) ///#pragma comment(linker, "/STACK:102400000,102400000") typedef long long ll; typedef long double ld; #define INF (1ll<<60)-1 #define Max 80*100000 using namespace std; int num[Max],cnt=1; //cnt代表的是节点的个数也是每个节点的下标 int d[Max][2]; void update(int x){ int p=1; for(int i=30;i>=0;i--){ if(d[p][(x>>i)&1]==0) d[p][(x>>i)&1]=++cnt; p=d[p][(x>>i)&1]; num[p]++; } } void update1(int x){ int p=1; for(int i=30;i>=0;i--){ p=d[p][(x>>i)&1]; num[p]--; } } int query(int x){ int ans=0; int p=1; for(int i=30;i>=0;i--){ int t=(x>>i)&1; if(num[d[p][1^t]]) p=d[p][1^t],ans+=(1<<i); else p=d[p][t]; } return ans; } char s[10]; int x; int main(){ int n; scanf("%d",&n); update(0); for(int i=1;i<=n;i++){ scanf("%s%d",s,&x); if(s[0]=='+'){ update(x); } else if(s[0]=='-'){ update1(x); } else { printf("%d\n",max(query(x),x)); } } return 0; }
可持久化01字典树:
经典问题:查询x与区间[L,R]哪个数异或值最大
题:https://www.luogu.com.cn/problem/P4735
题意:给定一个非负整数序列 {a},初始长度为n。
有 m 个操作,有以下两种操作类型:
1. A x:添加操作,表示在序列末尾添加一个数 x,序列的长度 n+1。
2. Q l r x:询问操作,你需要找到一个位置 p,满足l<=p<=r,使得:a[p]^a[p+1]^...^a[n]^x 最大,输出最大是多少。
分析:异或满足可减性,所以可以维护序列的异或前缀和s[ ]
那么 a[p]^a[p+1]^...^a[n]^x = s[p-1]^s[n]^x
此时查询转变为:已知val = s[n]^x,求一个p∈[l-1,r-1],使得s[p]^val最大
所以构建一棵可持久化trie,第i个版本为插入s[i]后的trie树
查找的时候就拿出第l-2和第r-1棵trie树进行操作
#include<bits/stdc++.h> using namespace std; const int M=6e5+5; struct TRIE{ //rt[i]为第i棵字典树的根,sum[i]表示i号节点被访问的次数 int trie[M*32][2],sum[M*32],val[M*32]; int rt[M],tot; void init(){ tot=0; rt[0]=++tot; } void insert(int u,int x){ rt[u]=++tot; int now=rt[u],pre=rt[u-1]; for(int i=31;i>=0;i--){ int id=(x>>i)&1; trie[now][id]=++tot; trie[now][id^1]=trie[pre][id^1]; now=trie[now][id]; pre=trie[pre][id]; sum[now]=sum[pre]+1; } val[now]=x; } int query(int l,int r,int x){ for(int i=31;i>=0;i--){ int id=(x>>i)&1; //优先寻找找与当前位不同的数 //表明当前节点在[l,r]区间内有数访问过 if(sum[trie[r][id^1]]-sum[trie[l][id^1]]>0) r=trie[r][id^1],l=trie[l][id^1]; else r=trie[r][id],l=trie[l][id]; } return val[r]^x; } }Trie; int s[M]; char op[2]; int main(){ int n,m; scanf("%d%d",&n,&m); for(int x,i=1;i<=n;i++){ scanf("%d",&x); s[i]=s[i-1]^x; Trie.insert(i,s[i]); } while(m--){ scanf("%s",op); int x,l,r; if(op[0]=='A'){ scanf("%d",&x); n++; s[n]=s[n-1]^x; Trie.insert(n,s[n]); } else{ scanf("%d%d%d",&l,&r,&x); l--,r--; if(l==0&&r==0) printf("%d\n",s[n]^x); else printf("%d\n",Trie.query(Trie.rt[max(l-1,0)],Trie.rt[r],s[n]^x)); } } }