1 条题解
-
0
自动搬运
来自洛谷,原作者为

wosile
疑罪从无。搬运于
2025-08-24 21:16:23,当前版本为作者最后更新于2024-06-05 11:13:39,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
UPD:修正笔误。
信息与未来真神吧?拿这种东西给小学生做?
我的做法可能偏复杂了。
观察到 ,对于每对括号,我们可以先对于其内部的字符串,计算出对于每一种初始位置,经过这个字符串后可能会到达哪里,以及可能会经过哪些位置。这两个信息可以用两个大小为 的 矩阵 表示。也就是说,一段字符串 就可以转化成一个矩阵二元组 。
对于单个字符
LRUD,我们可以提前把它们对应的这两个矩阵算出来。方便起见,我们把全部 个位置从 到 编号,比如说, 中, 就代表从编号为 的位置开始,经过字符串 所代表的操作序列,有没有可能经过编号为 的位置。
当两段操作序列 按顺序拼接在一起得到 时,假如我们从 号位置开始,我们会先经过 的位置 ,然后到达 的位置 ,然后再经过 的位置 ,到达 的位置 。
由此
并不显然有,$F_t(x,y)=\bigcup\limits_{r=1}^{nm}(M_t(x,r)\cap N_t(r,y))$ 和 $F_p(x,y)=(\bigcup \limits_{r=1}^{nm}M_t(x,r)\cup N_p(r,y))\cup M_p(x,y)$。我们发现这个东西很像矩阵乘法。具体来讲,若乘法为 矩阵乘法,那么 ,。显然是有结合律的,那不妨定义矩阵二元组的乘法如上,即 。
做这个乘法的复杂度是 的,但是我们可以通过
std::bitset或者unsigned long long或者__int128之类的东西压位,可以做到 。对于 的情况,显然整个字符串对应的矩阵二元组就是 个 相乘,即 。这里可以用快速幂,当然不用也无所谓。
对于 的情况,若把 看作一个邻接矩阵,则 相当于一个可达性矩阵,而 则相当于“在这个图上从 号位置开始走能到达的所有点 中,是否有点满足 ”。 可以通过 Floyd-Warshall 传递闭包(这里也可以压位除掉一个 )求出, 可以一并求出。
然后全拼起来就做完了,总复杂度 ,其中 是括号后附带的数字,。如果你有心情做矩阵快速幂可以做到 ,常数非常小。
这题既不入门,也不面试,但是它在入门与面试题库里面。综合算法难度,思维难度和代码难度,我觉得这应该是 B 题库最难的一题。
代码如下:
#include<bits/stdc++.h> using namespace std; const int dx[4]={0,0,-1,1}; const int dy[4]={-1,1,0,0}; char s[15][15],S[1005]; int n,m,N; inline int id(int x,int y){ return (x-1)*m+y; } inline int getx(int x){ return (x-1)/m+1; } inline int gety(int x){ return (x-1)%m+1; } struct command{ command(){ for(int i=1;i<=N;i++){ to[i].reset(); path[i].reset(); } } bitset<105>to[105],path[105]; void operator |=(command o){ for(int i=1;i<=N;i++){ to[i]|=o.to[i]; path[i]|=o.path[i]; } } }com[505]; int cnt=0,readpos=0; bitset<105>tr[105]; command concatenate(const command &x,const command &y){ command z; for(int i=1;i<=N;i++)z.path[i]=x.path[i]; for(int i=1;i<=N;i++)for(int j=1;j<=N;j++)if(x.to[i][j]){ z.to[i]|=y.to[j],z.path[i]|=y.path[j]; } return z; } command repeat(command &x,int k){ for(int i=1;i<=N;i++)tr[i].reset(); command ans; for(int i=1;i<=N;i++)ans.to[i][i]=1,ans.path[i][i]=1; if(k<1 || k>9){ ans|=x; for(int i=1;i<=N;i++)for(int j=1;j<=N;j++){ // Floyd 求最短路的时候我们有 dis[j][k] <--- dis[j][i]+dis[i][k],这里也是一样的道理。 if(ans.to[j][i]){ ans.to[j]|=ans.to[i]; ans.path[j]|=ans.path[i]; } } } else{ command tmp=x; while(k){ // 快速幂 if(k&1)ans=concatenate(ans,tmp); tmp=concatenate(tmp,tmp); k>>=1; } } return ans; } int Len; void walk(command &x,int dir){ for(int i=1;i<=N;i++)tr[i].reset(); for(int i=1;i<=N;i++)for(int j=1;j<=N;j++)if(x.to[i][j]){ int nx=getx(j)+dx[dir],ny=gety(j)+dy[dir]; if(nx>=1 && nx<=n && ny>=1 && ny<=m && s[nx][ny]=='.')tr[i][j+dx[dir]*m+dy[dir]]=1; else tr[i][j]=1; } for(int i=1;i<=N;i++)x.to[i]=tr[i],x.path[i]|=tr[i]; } void readCommand(command &x){ for(int i=1;i<=N;i++){ x.to[i][i]=x.path[i][i]=1; } while(readpos<Len && S[readpos]!=')'){ char c=S[readpos]; // LRUD 可以做到 N^2 而不是 N^3/w if(c=='L')walk(x,0); if(c=='R')walk(x,1); if(c=='U')walk(x,2); if(c=='D')walk(x,3); if(c=='('){ ++readpos; command &f=com[++cnt]; readCommand(f); ++readpos; char op=S[readpos]; x=concatenate(x,repeat(f,op-48)); } ++readpos; } } int main(){ scanf("%d%d",&n,&m); N=n*m; for(int i=1;i<=n;i++)scanf("%s",s[i]+1); scanf("%s",S); Len=strlen(S); readCommand(com[++cnt]); for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(s[i][j]=='#')putchar('#'); else if(com[1].path[id(1,1)][id(i,j)])putchar('+'); else putchar('.'); } putchar('\n'); } return 0; }
- 1
信息
- ID
- 10325
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者