技巧----异或

异或是一种重要的位运算,关于异或的问题一般有两种思路,一种是01字典树,另一种是线性基。

01字典树

01字典树一般用于解决最值问题,如选两个数异或,求最值。

可通过贪心的策略来寻找与 x异或结果最大的数,即优先找和 x二进制的未处理的最高位值不同的边对应的点,这样保证结果最大。

求最小值,即优先找和 x二进制的未处理的最高位值相同的边对应的点,这样保证结果最小。

可以在一个区间上建两颗01字典树,贪心的求极大值,记录经过节点的次数,用两个不同的数组记录,就可以建在同一颗字典树上了,有删除操作也要记录经过节点次数。

int tol; //节点个数 
LL val[32*MAXN]; //点的值 
int ch[32*MAXN][2]; //边的值 
 
void init()
{ //初始化 
    tol=1;
    ch[0][0]=ch[0][1]=0;
}
 
void add(LL x)
{ //往 01字典树中插入 x 
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int v=(x>>i)&1;
        if(!ch[u][v])
        { //如果节点未被访问过 
            ch[tol][0]=ch[tol][1]=0; //将当前节点的边值初始化 
            val[tol]=0; //节点值为0,表示到此不是一个数 
            ch[u][v]=tol++; //边指向的节点编号 
        }
        u=ch[u][v]; //下一节点 
    }
    val[u]=x; //节点值为 x,即到此是一个数 
}
 
LL query(LL x)
{ //查询所有数中和 x异或结果最大的数 
    int u=0;
    for(int i=32;i>=0;i--)
    {
        int v=(x>>i)&1;
        //利用贪心策略,优先寻找和当前位不同的数 
        if(ch[u][v^1]) u=ch[u][v^1];
        else u=ch[u][v];
    }
    return val[u]; //返回结果 
}

1.HDU 4825(模板题)

题目大意:在一组数中找跟某个数异或结果最大的数。

题解:直接套用模板,将数组中的数插入到 01字典树,对每一个数查询即可。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define LL long long
//#define int LL
const int maxn = 32e5+5;
const int M = maxn*20;
int t,n,q,m,tot;
int inf=0x3f3f3f3f3f;
int c[maxn][2],a[maxn];
int vis[maxn];

void init(){
    tot=0;
    c[0][0]=c[0][1]=0;
}

inline add(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        if(!c[u][v]){
            c[++tot][0]=c[tot][1]=0;
            vis[tot]=0;
            c[u][v]=tot;
        }
        u=c[u][v];
    }
    vis[u]=x;
}

inline query(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        if(c[u][!v]) u=c[u][!v];
        else u=c[u][v];
    }
    return vis[u];
}

inline int Read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f; ch=getchar();}
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline void writeln(int x)
{
    char s[64];
    if (x<0) putchar('-'),x=-x;
    if (!x) putchar('0'); else
    {
        int len=0;
        while (x) s[++len]=x%10+'0',x/=10;
          while (len) putchar(s[len--]);
    }
    putchar('\n');
}


int main()
{
    //ios::sync_with_stdio(false);
    //freopen("1.in","r",stdin);
    //freopen("out.out","w",stdout);
    //t=Read();
    scanf("%d",&t);
    for(int j=1; j<=t; ++j){
        init();
        //n=Read(),q=Read();
        scanf("%d%d",&n,&q);
        for(int i=0; i<n; ++i) {
            int x;
            scanf("%d",&x);
            add(x);
        }
        printf("Case #%d:\n",j);
        while(q--) {
            int x;
            scanf("%d",&x);
            int ans=query(x);
            printf("%d\n",ans);
        }
    }

    return 0;
}
View Code

 

2.HDU 5536

题目大意:在一个数组中找出 (s[i]+s[j])^s[k] 最大的值,其中 i、j、k 各不相同。

题解:建一颗01字典树,枚举i,j,先将i,j位置上的数从字典树中删除,用和在字典树上找最大值,之后在插入字典树中(数据水,暴力也可做)

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define LL long long
//#define int LL
const int maxn = 32e3+5;
const int M = maxn*20;
int t,n,q,m,tot;
int inf=0x3f3f3f3f;
int c[maxn][2],a[maxn];
int vis[maxn],cnt[maxn];

void init(){
    tot=0;
    c[0][0]=c[0][1]=0;
    memset(cnt,0,sizeof(cnt));
}

inline add(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        if(!c[u][v]){
            c[++tot][0]=c[tot][1]=0;
            vis[tot]=0;
            c[u][v]=tot;
        }
        u=c[u][v];
        cnt[u]++;
    }
    vis[u]=x;
}
void sub01(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        u=c[u][v];
        --cnt[u];
    }
}
inline query(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        if(c[u][!v]&&cnt[c[u][!v]]) u=c[u][!v];
        else u=c[u][v];
    }
    return vis[u]^x;
}

inline int Read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f; ch=getchar();}
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline void writeln(int x)
{
    char s[64];
    if (x<0) putchar('-'),x=-x;
    if (!x) putchar('0'); else
    {
        int len=0;
        while (x) s[++len]=x%10+'0',x/=10;
          while (len) putchar(s[len--]);
    }
    putchar('\n');
}


int main()
{
    //ios::sync_with_stdio(false);
    //freopen("1.in","r",stdin);
    //freopen("out.out","w",stdout);
    //t=Read();
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        init();
        for(int i=0; i<n; ++i) {scanf("%d",a+i);add(a[i]);}
        int ans=-inf;
        for(int i=0; i<n; ++i)
        for(int j=i+1; j<n; ++j){
            sub01(a[i]),sub01(a[j]);
            int x=a[i]+a[j];
            ans=max(ans,query(x));
            add(a[i]),add(a[j]);
        }
        printf("%d\n",ans);
    }

    return 0;
}
View Code

 

3.BZOJ 4260

题目大意:给你 n 个数,让你求两个不相交的区间元素异或后的和的最大值。

题解:求一遍异或前缀和pre[i]和后缀异或和suf[],先求dp[i](区间1~i中最大的区间异或值) ,dp[i]=max( dp[i-1] , max{ pre[i]^pre[j] }( j < i ) ), 然后就是一般的求ans= max{ dp[i-1] + max{ suf[i]^suf[j] } ( j > i ) }.

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define LL long long
//#define int LL
const int maxn = 4e5+5;
const int M = maxn*32;
int t,n,q,m,tot;
int inf=0x3f3f3f3f;
int c[M][2],a[maxn];
int pre[maxn],suf[maxn],dp[maxn];
int vis[M];

inline void init(){
    tot=0;
    c[0][0]=c[0][1]=0;
    //memset(cnt,0,sizeof(cnt));
}

inline void add(int x){
    int u=0;
    for(int i=32; i>=0; --i){
        int v=(x>>i)&1;
        if(!c[u][v]){
            c[++tot][0]=c[tot][1]=0;
            vis[tot]=0;
            c[u][v]=tot;
        }
        u=c[u][v];
    }
    vis[u]=x;
}

inline int query(int x){
    int u=0;
    for(int i=32; i>=0; --i){
        int v=(x>>i)&1;
        if(c[u][!v]) u=c[u][!v];
        else u=c[u][v];

    }
    return x^vis[u];
}


inline int Read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f; ch=getchar();}
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline void writeln(int x)
{
    char s[64];
    if (x<0) putchar('-'),x=-x;
    if (!x) putchar('0'); else
    {
        int len=0;
        while (x) s[++len]=x%10+'0',x/=10;
          while (len) putchar(s[len--]);
    }
    putchar('\n');
}


signed main()
{
    //ios::sync_with_stdio(false);
    //freopen("1.in","r",stdin);
    //freopen("out.out","w",stdout);
    //t=Read();
    //scanf("%d",&t);
    while(~scanf("%d",&n))
    {
        init();
        pre[0]=suf[n+1]=dp[0]=0;
        for(int i=1; i<=n; ++i) {
            scanf("%d",a+i);
            pre[i]=pre[i-1]^a[i];
        }
        for(int i=n; i>0; --i) suf[i]=suf[i+1]^a[i];
        add(pre[0]);
        for(int i=1; i<=n; ++i){
            dp[i]=max(dp[i-1],query(pre[i]));
            add(pre[i]);
        }
        init();
        int ans=0;
        //cout<<ans<<endl;
        add(0);
        for(int i=n; i>0; --i){
            ans=max(ans,dp[i-1]+query(suf[i]));
            add(suf[i]);
        }
        printf("%d\n",ans);
    }

    return 0;
}
View Code

 

4.POJ 3764

题目大意:在树上找一段路径(连续)使得边权相异或的结果最大。

题解:选一个节点为根rt,dfs求出其他节点到该节点路径的异或值,之后就转化为求任选两个数的最大异或和了

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;
#define LL long long
//#define int LL
const int maxn = 2e5+5;
const int M = maxn*32;


struct node
{ //链式前向星节点
    int to,next,val; //相邻节点,下一节点,边权
} mp[maxn*2];

int cnt,head[maxn]; //链式前向星

int t,n,q,m,tot=0;
int inf=0x3f3f3f3f;
int c[M][2],a[maxn];
int vis[M];

void add_edge(int u,int v,int w)
{ //加边
    mp[cnt].to=v;
    mp[cnt].val=w;
    mp[cnt].next=head[u];
    head[u]=cnt++;
}

void dfs(int u,int pre,int val){
    a[u]=val;
    for(int i=head[u]; i!=-1; i=mp[i].next){
        //int v=mp[i].to;
        if(mp[i].to!=pre){
            int tmp=val^mp[i].val;

            dfs(mp[i].to,u,tmp);
        }
    }
}
inline void init(){
    tot=0;
    vis[0]=0;
    memset(vis,0,sizeof(vis));
    memset(head,-1,sizeof(head));
    c[0][0]=c[0][1]=0;
    //memset(cnt,0,sizeof(cnt));
}

inline void add(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        if(!c[u][v]){
            c[++tot][0]=c[tot][1]=0;
            vis[tot]=0;
            c[u][v]=tot;
        }
        u=c[u][v];
    }
    vis[u]=x;
}

inline int query(int x){
    int u=0;
    for(int i=31; i>=0; --i){
        int v=(x>>i)&1;
        if(c[u][!v]) u=c[u][!v];
        else u=c[u][v];

    }
    return x^vis[u];
}


inline int Read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f; ch=getchar();}
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline void writeln(int x)
{
    char s[64];
    if (x<0) putchar('-'),x=-x;
    if (!x) putchar('0'); else
    {
        int len=0;
        while (x) s[++len]=x%10+'0',x/=10;
          while (len) putchar(s[len--]);
    }
    putchar('\n');
}


signed main()
{
    ios::sync_with_stdio(false);
    //freopen("1.in","r",stdin);
    //freopen("out.out","w",stdout);
    //t=Read();
    //scanf("%d",&t);

    //memset()
    while(~scanf("%d",&n))
    {
        init();

        cnt=0;
        for(int i=1; i<=n-1; ++i) {
            int x,y,val;
            scanf("%d%d%d",&x,&y,&val);
            add_edge(x,y,val),add_edge(y,x,val);
        }
        tot=0;
        dfs(0,0,0);
        init();
        add(0);
        int ans=-inf;
        //for(int i=0; i<n-1; ++i) cout<<a[i]<<" ";
        //cout<<endl;
        for(int i=0; i<n; ++i){
            ans=max(ans,query(a[i]));
            add(a[i]);
        }
        printf("%d\n",ans);
    }

    return 0;
}
View Code

 

5.2019年hdu多校第5场three arrays

#include<bits/stdc++.h>

using namespace std;
const int maxn = 1e5 + 10;
int t,n,m,tot,MLV;
int c[maxn*32][2],vis[maxn*32],a[maxn],cnt[2][maxn*32];

inline void init(){
    tot=0;
    c[0][1]=c[0][0]=vis[0]=0;
    //c[0][1]=c[0][0]=vis[1][0]=0;
    //memset(cnt,0,sizeof(cnt));
}

inline void add(int pos,int x){
    int u=0;
    for(int i=29; i>=0; --i){
        int v=(x>>i)&1;
        if(!c[u][v]){
            c[++tot][0]=c[tot][1]=vis[tot]=0;
            c[u][v]=tot;
        }
        u=c[u][v];
        ++cnt[pos][u];
        //cout<<pos<<" "<<cnt[pos][u]<<" "<<u<<endl;
    }
    //cout<<endl;
    vis[u]=x;
}

inline int query(){
    int u1=0,u2=0;
    for(int i=29; i>=0; --i){
        int v1=c[u1][0],v2=c[u1][1],v11=c[u2][0],v22=c[u2][1];
        if((cnt[0][v1]) && (cnt[1][v11]) ){
            u1=v1,u2=v11;
        }
        else if((cnt[0][v2]) && ( cnt[1][v22]) ){
            u1=v2,u2=v22;

        }
        else {
            if(cnt[0][v1]) u1=v1;
            else u1=v2;

            if(cnt[1][v22]) u2=v22;
            else u2=v11;
            //cout<<0<<" "<<1<<endl;
        }
        //cout<<u1<<" "<<u2<<" ";
        --cnt[0][u1],--cnt[1][u2];
        //cout<<cnt[0][u1]<<" "<<cnt[1][u2]<<endl;
    }
    //cout<<vis[u1]<<" "<<vis[u2]<<"\n\n";
    return vis[u1]^vis[u2];
}
inline int Read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-f; ch=getchar();}
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}

inline void writeln(int x)
{
    char s[64];
    if (x<0) putchar('-'),x=-x;
    if (!x) putchar('0'); else
    {
        int len=0;
        while (x) s[++len]=x%10+'0',x/=10;
          while (len) putchar(s[len--]);
    }
    //putchar('\n');
}
int main() {
    //freopen("1.in","r",stdin);
    //freopen("out.out","w",stdout);
    //scanf("%d", &t);
    t=Read();
    while (t--) {
        init();
        //scanf("%d",&n);
        int ma=-1,x;
        n=Read();
        for(int i=1; i<=n; ++i){
            x=Read();
            add(0,x);
        }
        for(int i=1; i<=n; ++i){
            x=Read();
            add(1,x);
        }
        //for(int i=1; i<=n; ++i) add(1,a[i]);
        //cout<<cnt[0][0]<<" "<<cnt[1][0]<<endl;
        for(int i=1; i<=n; ++i) a[i]=query();
        sort(a+1,a+n+1);
        for(int i=1; i<=n; ++i) {
            writeln(a[i]);
            if(i==n) putchar('\n');else putchar(' ');
        }
    }
}
View Code

 

posted @ 2020-06-02 10:05  mrdushe  阅读(200)  评论(0编辑  收藏  举报