01字典树

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 long
int 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 long
const 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 long
const 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 long
int 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 268435456
int 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值