题目链接: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);
}