01字典树

来源:互联网 发布:java云计算方向学什么 编辑:程序博客网 时间:2024/05/26 05:51

Xor Sum HDU - 4825

题意:一个集合,集合中包含了N个正整数,随后 发起M次询问,每次询问中包含一个正整数 S ,之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大。

分析:用01字典树,这是一颗二叉树,只有01。我们用静态数组来建树。对于每次询问,我们先得到一个值temp(temp和k的异或最大),然后在字典树中去查找这个数,如果当前点不存在,就往反方向走。其中处理的时候,所有数的长度都要相同。

#include <iostream>#include <cstring>#include <algorithm>#include <cmath>#include <cstdio>#include <vector>using namespace std;#define LL long longint node[3200010][2];LL zz[100];int h=0,rt=0;void add(LL x){    rt=0;    for(int i=31;i>=0;i--)    {        int g=x&(1<<i)?1:0;        if(node[rt][g]==-1)            node[rt][g]=++h;        rt=node[rt][g];    }}LL lookfor(LL x){    int rt=0;    LL ans=0;    for(int i=31;i>=0;i--)    {        int g=x&(1<<i)?1:0;        if(node[rt][g]==-1) g=g^1;//长度相同,反方向一定存在        rt=node[rt][g];        ans=(ans<<1)+g;//记录值    }    return ans;}int main(){    int T,case1=1;    scanf("%d",&T);    zz[0]=1;    for(int i=1;i<=32;i++)//用数组处理,因为1<<32 是负数。        zz[i]=zz[i-1]<<1;   // cout<<zz[32]<<endl;    while(T--)    {        h=0;        memset(node,-1,sizeof(node));        int n,m;        scanf("%d %d",&n,&m);        for(int i=0;i<n;i++)        {            LL x;            scanf("%I64d",&x);            add(x);        }        printf("Case #%d:\n",case1++);        for(int i=0;i<m;i++)        {            LL x;            scanf("%I64d",&x);            LL temp=x^(zz[32]-1);            printf("%I64d\n",lookfor(temp));        }    }    return 0;}

B - Chip Factory HDU - 5536

题意:给你一个数列,s1,s2,s2…sn ,然后问你
maxi,j,k(si+sj)⊕sk 的值 (which i,j,ki,j,k are three different integers between 11 and nn.)

分析:和第一题差不多,先将数列中的所有数都丢进字典树,然后开始枚举si,sj,去找sk,在枚举的时候先将si,sj在字典树中删除,然后找sk,找完之后再加上就行了。

#include <iostream>#include <cstring>#include <cmath>#include <algorithm>#include <cstdio>#include <queue>#include <vector>using namespace std;#define LL long longconst int maxn = 31000;int node[maxn][2];int a[1010],y[50],val[maxn];int tot=1;void sert(int x,int d){    int u=0;    for(int i=30;i>=0;i--)    {        int g=(x>>i)&1;        if(!node[u][g]){            node[u][g]=tot++;        }        val[node[u][g]]+=d;        u=node[u][g];    }}void del(int x){    int u=0;    for(int i=30;i>=0;i--)    {        int g=(x>>i)&1;        val[node[u][g]]-=1;        u=node[u][g];    }}int lookfor(int x){    int u=0,ans=0;    for(int i=30;i>=0;i--)    {        int g=(x>>i)&1;        if(!node[u][g]||!val[node[u][g]]) g=g^1;        u=node[u][g];        ans=(ans<<1)+g;    }    return ans;}int main(){    int T;    scanf("%d",&T);    y[0]=1;    for(int i=1;i<=31;i++)        y[i]=y[i-1]<<1;    while(T--)    {        memset(val,0,sizeof(val));        memset(node,0,sizeof(node));        memset(a,0,sizeof(a));        int n;        tot=1;        scanf("%d",&n);        for(int i=0;i<n;i++)        {            scanf("%d",&a[i]);            sert(a[i],1);        }        int ans=0;        for(int i=0;i<n;i++)        {            for(int j=i+1;j<n;j++)            {                int temp=(a[i]+a[j])^(y[31]-1);                del(a[i]);                del(a[j]);                int k=lookfor(temp);                ans=max(ans,k^(a[i]+a[j]));                sert(a[i],1),sert(a[j],1);            }        }        printf("%d\n",ans);    }    return 0;}

C - The xor-longest Path POJ - 3764

题意:给你一颗无根树,然后问你其中的任何一条路径的权值异或最大是多少。

分析:我们先建图,确定一个根,dfs来求所有点到原点的异或和,然后任何一条路径的异或 和就是两端点到根的异或相乘。

#include <iostream>#include <cstring>#include <cmath>#include <algorithm>#include <cstdio>#include <queue>#include <vector>using namespace std;#define LL long longconst int maxn = 100005;struct edge{    int to,pre;    LL w;};edge e[maxn*2];int head[maxn*2],vis[maxn];LL a[maxn];LL c[maxn];int h=0;void add(int u,int v,LL w){    e[h].to=v,e[h].pre=head[u],e[h].w=w;head[u]=h;    h++;}void dfs(int x,LL z){    for(int i=head[x];i>-1;i=e[i].pre)    {        if(!vis[e[i].to])        {            a[e[i].to]=z^e[i].w;            vis[e[i].to]=1;            z=a[e[i].to];            dfs(e[i].to,z);            z=z^e[i].w;        }    }}int node[maxn*35][2],val[maxn*35];int tot=1;void sert(int x,int d){    int u=0;    for(int i=30;i>=0;i--)    {        int g=(x>>i)&1;        if(!node[u][g])            node[u][g]=tot++;        u=node[u][g];        val[u]+=d;    }}LL lookfor(LL x){    int u=0;    LL ans=0;    for(int i=30;i>=0;i--)//这里和下面都要统一啊啊啊    {        LL g=(x>>i)&1;        if(!node[u][g]||!val[node[u][g]]) g=g^1;        u=node[u][g];        ans=(ans<<1)+g;    }    return ans;}int main(){    int n;    c[0]=1;    for(int i=1;i<=31;i++)        c[i]=c[i-1]<<1;    while(scanf("%d",&n)!=EOF)    {        h=0,tot=1;        memset(e,0,sizeof(e));        memset(head,-1,sizeof(head));        memset(vis,0,sizeof(vis));        memset(node,0,sizeof(node));        memset(a,0,sizeof(a));        memset(val,0,sizeof(val));        for(int i=0;i<n-1;i++)        {            int u,v;LL w;            scanf("%d %d %I64d",&u,&v,&w);            add(u,v,w);            add(v,u,w);        }        vis[0]=1;        dfs(0,0);        for(int i=0;i<n;i++)            sert(a[i],1);        LL ans=0;        for(int i=0;i<n;i++)        {            sert(a[i],-1);            LL temp=a[i]^(c[31]-1);            ans=max(ans,(a[i]^lookfor(temp)));            sert(a[i],1);        }        printf("%I64d\n",ans);    }    return 0;}

bzoj 4260 Codechef REBXOR

这里写图片描述

分析:从前求一遍异或和,然后从后求一遍异或和,就行了。哎,数组开小了,WA的心痛。。

#include <iostream>#include <cstring>#include <cmath>#include <algorithm>#include <cstdio>#include <queue>#include <vector>using namespace std;#define LL long longint node1[31*400005][2];int a[400005],b[400005],b2[400005],c[400005];int tot1=1;int dp1[400005];void sert1(int x,int d){    int u=0;    for(int i=31;i>=0;i--)    {        int g=(x>>i)&1;        if(!node1[u][g]) node1[u][g]=tot1++;        u=node1[u][g];    }}int findf1(int x){    int u=0,ans=0;    for(int i=31;i>=0;i--)    {        int g=(x>>i)&1; g=g^1;        if(!node1[u][g]) g=g^1;        u=node1[u][g];        ans=(ans<<1)+g;    }    return ans;}int main(){    int n;    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&a[i]);        b[i]=a[i]^b[i-1];    }    int h=1;    for(int i=n;i>=1;i--){        c[h]=a[i];        b2[h]=b2[h-1]^c[h];        h++;    }    sert1(0,1);    for(int i=1;i<=n;i++)    {        int t1=findf1(b[i]);        t1=t1^b[i];        dp1[i]=max(dp1[i-1],t1);        sert1(b[i],1);    }    memset(node1,0,sizeof(node1));    tot1=1;    int ans=0;    sert1(0,1);    for(int i=1;i<=n;i++)    {        int t2=findf1(b2[i]);        t2=t2^b2[i];        ans=max(ans,t2+dp1[n-i]);        sert1(b2[i],1);    }    printf("%d\n",ans);    return 0;}

Best Division HDU - 5845
题意:给你一个数列,然后问你最多可以分成多少段,其中每段的长度不超过l,每段的异或和最大不超过x。

分析:有dp的思想,dp[i]:表示到i点的时候最多可以分成多少段,dp[i]=dp[j]+1(j < i&&s[i]*s[j] < =x)
到i了,我们就在字典树中去找s[i]与x的对应关系,如果s[i]找到了能和他相乘符合条件的,就将s[i]插入01字典树。…

#include <iostream>#include <cstring>#include <cstdio>#include <algorithm>#include <cmath>#include <vector>using namespace std;#define LL long long#define mod 268435456int n,x,l;LL p,q;const int maxn = 100005;LL a[maxn];int node[32*maxn][2],val[maxn*32],dp[maxn*32],fa[maxn*32];LL sum[maxn];int tot=1;void sert(int s,int c){    int u=0;    for(int i=30;i>=0;i--)    {        int g=(s>>i)&1;        if(!node[u][g]){             node[u][g]=tot++;             fa[node[u][g]]=u;        }        u=node[u][g];        val[u]+=1;        dp[u]=max(dp[u],c);    }}int findf(int s){    int u=0,ans=-1,flag=0;    for(int i=30;i>=0;i--)    {        int g=(s>>i)&1;        int gg=(x>>i)&1;        if(gg){            if(node[u][g]&&val[node[u][g]]) ans=max(ans,dp[node[u][g]]);            if(node[u][g^1]&&val[node[u][g^1]]) g=g^1;        }        u=node[u][g];        if(!u||!val[u]) break;        if(i==0) {flag=1;ans=max(ans,dp[u]);}    }    //if(flag) return ans;    return ans;}void del(int s){    int u=0,flag=0;    for(int i=30;i>=0;i--)    {        int g=(s>>i)&1;        if(!node[u][g])        {            flag=1;break;        }    }    u=0;    if(!flag)    {        for(int i=30;i>=0;i--)        {            int g=(s>>i)&1;            val[node[u][g]]-=1;            u=node[u][g];        }    }    if(!val[u])    {        dp[u]=0;        while(u)        {            u=fa[u];            dp[u]=0;            if(node[u][0]&&val[node[u][0]]) dp[u]=dp[node[u][0]];            if(node[u][1]&&val[node[u][1]]) dp[u]=max(dp[u],dp[node[u][1]]);        }    }}int main(){    int T;    scanf("%d",&T);    while(T--)    {        tot=1;        memset(node,0,sizeof(node));        memset(val,0,sizeof(val));        memset(dp,0,sizeof(dp));        memset(sum,0,sizeof(sum));        memset(a,0,sizeof(a));        memset(fa,0,sizeof(fa));        scanf("%d %d %d",&n,&x,&l);        scanf("%I64d %I64d %I64d",&a[1],&p,&q);        for(int i=2;i<=n;i++)            a[i]=(a[i-1]*p%mod+q)%mod;        for(int i=1;i<=n;i++)            sum[i]=sum[i-1]^a[i];        sert(0,0);        int ans=0;        int c=0;        for(int i=1;i<=n;i++)        {            if(i-l-1>=0) del(sum[i-l-1]);            c=findf(sum[i])+1;            if(c>0) sert(sum[i],c);        }        printf("%d\n",c);    }    return 0;}