原题传送门
题意
给定一个
n
∗
m
n*m
n∗m 的棋盘,问从
(
1
,
1
)
(1,1)
(1,1)点走到
(
n
,
m
)
(n , m)
(n,m)点并且所经过的值异或和为
k
k
k的路径数是多少。
(
1
≤
n
,
m
≤
20
,
0
≤
k
≤
(1≤n,m≤20, 0≤k≤
(1≤n,m≤20,0≤k≤ 1018 ,
0
≤
0≤
0≤
a
i
,
j
a_{i,j}
ai,j
≤
≤
≤ 1018
)
)
)
思路
直接从
(
1
,
1
)
(1,1)
(1,1) 搜到
(
n
,
m
)
(n,m)
(n,m)是肯定会超时的,所以可以先从
(
1
,
1
)
(1,1)
(1,1) 搜到 棋盘的副对角线,并记录到对角线上各点的异或值的个数,然后从终点
(
n
,
m
)
(n,m)
(n,m)反着搜,搜到副对角线上结束,每次给答案
c
n
t
cnt
cnt加上能使异或和为
k
k
k的路径数。
比如这个
3
∗
3
3*3
3∗3的棋盘
我们先从起点搜到
5
,
10
,
12
5,10,12
5,10,12,记录好各个异或值的个数后,再从终点搜到这三个值,匹配相应的异或值即可。
细节处理,应该搜到
x
+
y
=
m
a
x
(
n
,
m
)
+
1
x + y = max(n , m) + 1
x+y=max(n,m)+1 时结束,不然还要特判
1
∗
1
1*1
1∗1棋盘的情况。
(依然在补几个月前的题目,菜菜)
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long LL;
typedef pair<int,int> P;
const int N = 21;
int n , m;
LL k , cnt , a[N][N];
map<LL,LL>mp[N][N];//记录每个异或值的个数
int dx[4] = {1 , 0 , -1 , 0};
int dy[4] = {0 , 1 , 0 , -1};
void dfs1(int x , int y , LL val){
LL now = val ^ a[x][y];
if(x + y == max(n , m) + 1){
mp[x][y][now] ++;
return;
}
for(int i = 0 ; i < 2 ; i ++){
int xx = x + dx[i] , yy = y + dy[i];
if(xx >= 1 && xx <= n && yy >= 1 && yy <= m)
dfs1(xx , yy , now);
}
return;
}
void dfs2(int x , int y , LL val){
if(x + y == max(n , m) + 1){
cnt += mp[x][y][k ^ val];//加上对应异或值的路径数
return;
}
LL now = val ^ a[x][y];
for(int i = 2 ; i < 4 ; i ++){
int xx = x + dx[i] , yy = y + dy[i];
if(xx >= 1 && xx <= n && yy >= 1 && yy <= m)
dfs2(xx , yy , now);
}
return;
}
int main(){
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin >> n >> m >> k;
for(int i = 1 ; i <= n ; i ++)
for(int j = 1 ; j <= m ; j ++)
cin >> a[i][j];
dfs1(1 , 1 , 0LL);
dfs2(n , m , 0LL);
cout << cnt;
return 0;
}