遇见多少就写多少吧…
1、双向搜索
来个例题:CF1006F Xor-Paths
题意简述:有一个n*m的矩阵,从(1,1)出发到(n,m),只能向右或向下走,并取经过方格里的数,问有多少种路径使得路径上的数异或和为k
因为
a
i
a_i
ai太大了,用DP+map的话,时间复杂度O(nm
a
i
a_i
ai)很明显超时
我们发现n、m很小,只有20,就算是爆搜也就是
2
n
−
1
+
m
−
1
2^{n-1+m-1}
2n−1+m−1也能骗不少分
那我们可不可以优化呢???
下面普及一些芝士 :
- 异或结合律:a^ b ^ c = a ^ (b ^ c)
- a ^ b=c < ----> c ^ b=a
-
a
2
a^2
a2>=
(
0.5
a
)
2
(0.5a)^2
(0.5a)2+
(
0.5
a
)
2
(0.5a)^2
(0.5a)2
如果我们把搜索的深度降为一半,时间复杂度变成O(2* 2 ( n − 1 + m − 1 ) / 2 2^{(n-1+m-1)/2} 2(n−1+m−1)/2)最多也就是 2 20 2^{20} 220可以跑过,
我们发现了只要你走了(n-1+m-1)/2的话,你一定是在矩形的对角线上,加一个map存一下值,就可以过了
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
long long n,m,k,a[30][30],mid,ans;
map<long long,long long>vis[30][30];
void dfs1(long long x,long long y,long long now){
if (x+y-2==mid){
vis[x][y][now]++;
// printf("dfs1:%lld %lld\n",x,y);
return ;
}
if (x<n)dfs1(x+1,y,now^a[x+1][y]);
if (y<m)dfs1(x,y+1,now^a[x][y+1]);
return ;
}
void dfs2(long long x,long long y,long long now){
if (n-x+m-y==n+m-2-mid-1){
// printf("dfs2:%lld %lld\n",x,y);
if (y>1)ans+=vis[x][y-1][k^now];
if (x>1)ans+=vis[x-1][y][k^now];
return ;
}
if (y>1)dfs2(x,y-1,now^a[x][y-1]);
if (x>1)dfs2(x-1,y,now^a[x-1][y]);
return ;
}
int main(){
scanf("%lld%lld%lld",&n,&m,&k);
mid=(n+m-2)>>1;
// printf("!!!%lld\n",mid);
for (int i=1;i<=n;i++){
for (int j=1;j<=m;j++){
scanf("%lld",&a[i][j]);
}
}
if (n==1&&m==1){
if (a[1][1]==k)printf("1\n");
else printf("0\n");
return 0;
}
dfs1(1,1,a[1][1]);
dfs2(n,m,a[n][m]);
printf("%lld",ans);
return 0;
}