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
",ans);
    }
    return 0;
}
View Code

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:
",p);
        for(int i=1;i<=m;i++){
            int x;
            scanf("%d",&x);
            printf("%d
",Trie.query(x));
        }
    
    }
    return 0;
}
View Code

最长异或路径:树上异或最大路径是多少

题: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
",ans);
    return 0;
    
}
View Code

题: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
",max(query(x),x));
        }
    }
    return 0;
}
View Code

可持久化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
",s[n]^x);
            else
                printf("%d
",Trie.query(Trie.rt[max(l-1,0)],Trie.rt[r],s[n]^x));
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/starve/p/12237165.html