CF715D Create a Maze 题解

· · 题解

放一个基于 Fibonacci 序列的做法 qwq。

同时是本喵的第三篇黑题题解,写的不好还请各位大神谅解喵。

首先我们可以通过用 Fibonacci 序列的每项上、下连放两个的性质,构造出这样一个网格:

由[齐肯多夫定理](https://baike.baidu.com/item/%E9%BD%90%E8%82%AF%E5%A4%9A%E5%A4%AB%E5%AE%9A%E7%90%86/7612155),任何正整数都能被表示为**若干个不相邻的 $f_i$ 的和**。于是构造这样的网格,维护两条路(黄色格子): ![](https://cdn.luogu.com.cn/upload/image_hosting/lknielog.png) 右下角留白的格子是终点,紫色格子是我们要完全堵死的部分(不允许存在任何一条经过紫色格子的路抵达终点)。 **用墙壁隔开任意两种相邻颜色**(白色除外),这样整个就成了一条死路。我们为了在黄色路径部分造出恰好为 $n$ 的方案数,考虑对 $n$ 进行 Fibonacci 拆分,打开对应蓝格子与黄色路径部分的墙即可。 比如随便举个例,$n = 215 = 144+55+13+3$,那么在下图中画红色线段的部分就是打开的墙: ![](https://cdn.luogu.com.cn/upload/image_hosting/tmt2no7p.png) 构造结束!这样你就可以把这题过掉了……吗? 并非,随便写个程序跑一下,或者你挨个算一下(当做红线段只有 $1$ 条的情况),会发现最坏情况下需要 $350$ 左右个墙,即使计算时存在部分重复为 $340$ 个,也是不满足题目要求 $\le 300$ 个的。 怎么办,难道思路完全错了吗?没有。思考我们在哪里的开销最大,会发现,紫色与黄色的交界处每一小块都得挡住,非常浪费,而我们的目标只是不让经过紫色格子的路径成为最终路径而已。 那能优化吗?当然能,发现只需用下图绿色线段做墙,就可以将这部分的开销直接减半: ![](https://cdn.luogu.com.cn/upload/image_hosting/0wy1zf06.png) 为什么这样也能挡住啊?以左下角部分举例,发现一旦进入紫色区域,想出去只能往上走绕过去,但是题目要求只能往右或往下走,所以是不可能的。右上角部分同理。 于是你再重新算一下,发现节省了 $88$ 左右个墙壁的开销,最终墙壁个数控制在 $260$ 左右,肯定满足题目 $\le 300$ 的要求。 于是就做完了。 ::::success[code && [submission](https://codeforces.com/contest/715/submission/378202008)] ```cpp #include<bits/stdc++.h> #define LL long long #define UInt unsigned int #define ULL unsigned long long #define LD long double #define pii pair<int,int> #define pLL pair<LL,LL> #define pDD pair<LD,LD> #define fr first #define se second #define pb push_back #define isr insert #define _i128 __int128 using namespace std; const int N = 105; LL T,n=46,m=47,cnt; LL f[N],fup=88; bool vD[N][N],vR[N][N]; LL read(){ LL su=0,pp=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();} while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();} return su*pp; } int main(){ T=read(); f[1]=1,f[2]=1; for(LL i=3;i<=fup;i++)f[i]=f[i-1]+f[i-2]; for(LL i=3,j=1;i<=n;((i%2)==(j%2)?i++:j++)) if((i%2)==(j%2))vD[i-1][j]=1,vR[i][j]=1; else vD[i][j]=1; for(LL i=1,j=4;j<=m;((i%2)==(j%2)?i++:j++)) if((i%2)==(j%2))vR[i][j]=1; else vR[i][j-1]=1,vD[i][j]=1; vD[n-1][m-2]=1,vD[n-1][m-1]=1,vR[n-1][m-1]=1; for(LL i=fup;i>=1;i--){ if(T<f[i])continue; T-=f[i]; if(i%2)vD[(i+1)/2+1][(i+1)/2]=0; else vR[i/2][i/2+2]=0; } for(int i=1;i<n;i++) for(int j=1;j<m;j++) cnt+=vD[i][j],cnt+=vR[i][j]; cout<<n<<" "<<m<<"\n"<<cnt<<"\n"; for(int i=1;i<n;i++) for(int j=1;j<m;j++){ if(vD[i][j])cout<<i<<" "<<j<<" "<<i+1<<" "<<j<<"\n"; if(vR[i][j])cout<<i<<" "<<j<<" "<<i<<" "<<j+1<<"\n"; } return 0; } ``` :::: 是 Ad-hoc 高质量好题喵,值得一做。 还有拆进制的做法,也挺巧的,大家都太强啦 /bx。 如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太感谢啦!