题解:AT_abc402_f [ABC402F] Path to Integer

· · 题解

分析

很微妙的折半搜索题。

首先我们发现每经过一个格子后它的贡献是一定的,找规律可知 A_{i,j} 的贡献为 10^{2n-i-j}A_{i,j} ,这就变成了一个方格取数?

但是它要你求取模后的最大值,不过我们发现如果上一格如果是模后最大值,但是下一个格子加上之后就不一定了。不能 dp ,只能爆搜?

考虑折半搜索,Meet in middle。这个东西简单来说就是先将现在要搜索的东西平分成两段,最后再整合信息,输出答案。这题中怎么分? 用矩阵右上到左下的对角线将它分为两半,对于每个对角线上的数,计算起点和终点到它的路径的所有可能结果,分别存入两个 set 。然后整合结果。遍历第一个 set ,也就是起点到中点的路径,再第二个 set 中找中点到终点的路径。设刚才遍历到的值为 $x$ , $0 \leq x < M$ 。并且第二个 set 中只有两个数有可能成为最好路径。第一个是小于 $M-x$ 的最大值,第二个是直接的最大值。想一想,为什么? ## Code 实现中用了 set 中内置的二分函数,用法和普通的 upper_bound 和 lower_bound 差不多。赛时代码有点丑。仅供参考。 ```c++ #include <bits\stdc++.h> #define int long long using namespace std; int n,m,ans,a[21][21],p[39]={1}; set<int> s[21][21]; void dfs(int x,int y,int X,int Y,int S) { S=(S+a[x][y])%m;//记得取模 if(x==X&&y==Y) { s[x][y].insert(S);//加入set return; } if(x<X) dfs(x+1,y,X,Y,S); if(y<Y) dfs(x,y+1,X,Y,S); }//从起点到中点的搜索 void dfs2(int x,int y,int X,int Y,int S) { S=(S+a[x][y])%m; if(x==X&&y==Y) { s[x][y].insert(S);//加入set return; } if(x>X) dfs2(x-1,y,X,Y,S); if(y>Y) dfs2(x,y-1,X,Y,S); }//从中点到终点的搜索 int main() { cin>>n>>m; for(int i=1;i<=2*n-2;i++) p[i]=10*p[i-1]%m;//预处理10的幂 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { cin>>a[i][j]; a[i][j]=a[i][j]*p[2*n-i-j]%m;//计算单点贡献 } if(n==1) { cout<<a[1][1]%m; return 0; }//记得特判,不然wa两个点 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) { if(i+j-1==n) dfs(1,1,i,j,0); if(i+j-1==n+1) { dfs2(n,n,i,j,0); for(auto k:s[i][j]) { if(i>1)//最好判一下边界 { auto p=s[i-1][j].lower_bound(m-k); if(p!=s[i-1][j].begin()) ans=max(ans,k+*prev(p));//情况1 ans=max(ans,k+*s[i-1][j].rbegin()-m);//情况2 } if(j>1) { auto p=s[i][j-1].lower_bound(m-k); if(p!=s[i][j-1].begin()) ans=max(ans,k+*prev(p));//情况1 ans=max(ans,k+*s[i][j-1].rbegin()-m);//情况2 } } } } cout<<ans;//华丽的输出qwq~ return 1; } ``` 呼,写完了。看懂记得点赞哦。