(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦
题意:传送门
原题目描述在最下面。
题目大意就是给你一个n*m的矩阵,每个点都有权值。问你从点[1, 1]走到[n, m]路径上所有点权异或和为k的路径有多少种。注意只能向下和向右走。
思路:
大佬说这题暴力就行了。然而我最暴力的搜索方法却超时了。
这才了解到原来大佬的暴力是折半搜索。
折半搜索就是把最暴力从[1, 1]搜索到[n, m]的搜索方法,改成,从[1, 1]搜索到中间一个过程,再从[n, m]搜索到中间那个过程。这样的两次搜索,复杂度降低了居多。
我选取的中间过程是一条斜对角线的平行线:i + j = (n + m)/2 + 1
.
正向搜索过程中用一个map记录搜索到低i行异或和值为x的数量。
反向搜索过程就是计数了。
(我测试了一下,折半搜索的时间复杂度大概在2e6左右,逃~
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5+7;
const int INF = 0x3f3f3f3f;
const int mod = 998244353;
int n, m;
LL k, ans, ar[25][25];
unordered_map<LL, LL> cnt[N];
void dfs1(int i, int j, LL val){
if(i > n || j > m || i < 1 || j < 1)return;
val ^= ar[i][j];
if(i + j == (m + n) / 2 + 1){
//printf("%d %d %lld\n", i,j,val);
cnt[i][val]++;
return;
}
dfs1(i, j + 1, val);
dfs1(i + 1, j, val);
}
void dfs2(int i, int j, LL val){
if(i > n || j > m || i < 1 || j < 1)return;
if(i + j == (m + n) / 2 + 1){
ans += cnt[i][k ^ val];
return;
}
val ^= ar[i][j];
dfs2(i - 1, j, val);
dfs2(i, j - 1, val);
}
int main(int argc, char const *argv[]){
while(~scanf("%d%d%lld", &n, &m, &k)){
for(int i = 1; i <= n; ++i){
cnt[i].clear();
for(int j = 1; j <= m; ++j){
scanf("%lld", &ar[i][j]);
}
}
ans = 0;
dfs1(1, 1, 0);
dfs2(n, m, 0);
printf("%lld\n", ans);
}
return 0;
}