牛客练习赛37——筱玛的迷阵探险

题目链接:https://ac.nowcoder.com/acm/contest/545/D

这个题让我了解了01字典树,这是用于求异或最大值的特殊的字典树

模板:https://blog.csdn.net/zuzhiang/article/details/79872805

和普通字典树一样,不过01字典树把插入字符改成了插入二进制串的每一位(0或1),这刚好适应异或这个操作:一位一位地比较,贪心地在01字典树中向下递归,每个节点都有0和1两条边(除了叶子节点,叶子节点记录值),比如,当前位为1,那我们就选择0这条边。

思路参考:https://blog.csdn.net/qq_41750091/article/details/86481004

那么这个题目中,如果用dfs遍历暴力地插入整个图,时间复杂度为O(2^(40))肯定超时,那么就在对角线(从左下到右上)进行操作,对每个对角线的节点构建成一个01字典树,并构建两个vector,一个保存从(1,1)到达这个节点的每种情况,一个保存从(n,n)到达这个节点的每种情况,那么对于每个节点,将其中一个vector中的值插入,再查询另一个vector得到最大答案,那么所有节点的最大答案就是答案,时间复杂度为O(2^20))

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

const int maxn=1e5+10;
typedef long long ll;
ll A[24][24];
ll tol;
ll val[32*maxn];
ll ch[32*maxn][2];//01字典树
ll n,e;
vector<ll>v1[24],v2[24];//从起点和终点开始的每个对角线的值,最后取最大

void init(){
    tol=1;
    memset(val,0,sizeof(val));
    memset(ch,0,sizeof(ch));
}
void Insert(ll x){
    //向01字典树中插入x
    ll u=0;
    for(ll i=32;i>=0;i--){
        ll 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;//保存节点值
}
ll query(ll x){
    //查询所有数中与x异或最大的数
    ll u=0;
    for(ll i=32;i>=0;i--){
        ll v=(x>>i)&1;
        //利用贪心策略,优先寻找和当前位不同的数
        if(ch[u][v^1]) u=ch[u][v^1];
        else u=ch[u][v];
    }
    return val[u];
}
//从(1,1)开始,直到对角线结束,cnt为步数,当cnt==n时一定到对角线
void dfs1(int x,int y,ll num,int cnt){
    if(cnt==n){
        num^=A[x][y];
        v1[x].push_back(num);
        return;
    }
    dfs1(x+1,y,num^A[x+1][y],cnt+1);
    dfs1(x,y+1,num^A[x][y+1],cnt+1);
}
//从(n,n)开始,直到对角线结束
void dfs2(int x,int y,ll num,int cnt){
    if(cnt==n){
        //num^=A[x][y];
        v2[x].push_back(num);
        return;
    }
    dfs2(x-1,y,num^A[x-1][y],cnt+1);
    dfs2(x,y-1,num^A[x][y-1],cnt+1);
}
int main(){
    scanf("%lld%lld",&n,&e);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            scanf("%lld",&A[i][j]);
        }
    }
    dfs1(1,1,e^A[1][1],1);
    dfs2(n,n,A[n][n],1);
    ll ans=0;
    for(int i=1;i<=n;i++){
        init();
        for(int j=0;j<v1[i].size();j++){
            Insert(v1[i][j]);
        }
        for(int j=0;j<v2[i].size();j++){
            ans=max(ans,query(v2[i][j])^v2[i][j]);//query(v2[i][j])返回与v2[i][j]异或最大的v1[x][y]
        }
    }
    printf("%lld\n",ans);
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值