[题解]细胞自动机
给定一个长度为\(n\)的\(01\)串\(s\),用于表示一个环上的细胞的初始状态,其中第\(1\)个细胞与第\(2\)个、第\(n\)个细胞相邻;第\(n\)个细胞与第\(1\)个和第\(n-1\)个相邻。\(0\)表示细胞死亡,\(1\)表示细胞存活。接下来给定\(t\)轮操作,每一轮操作,根据上一轮细胞的状态更改此轮的状态。规则如下:
- 如果上一轮中与该细胞相邻的\(2\)个细胞中正好有\(1\)个存活,那么此轮中该细胞存活。否则此轮中该细胞死亡。
给定正整数\(t\),请输出一个\(01\)串,表示\(t\)轮变化后每个细胞的存活状态。
数据范围:对于\(50\%\)的数据,保证\(3\le n\le 15\),\(1\le T\le 10^{15}\)。
对于所有数据,保证\(3\le n\le 10^5\),\(1\le T\le 10^{15}\)
样例:
Sample #1
Input
7 1 0000001
Output
1000010
Sample #2
Input
5 3 01011
Output
10100
50pts - 矩阵快速幂解法 - \(O(n^3\log t)\)
考虑用广义矩阵乘法代替普通矩阵乘法:把两数相乘改为两数取与,两数相加改为两数去异或。
则可以构建初始矩阵和递推矩阵(拿\(6\)阶举例,其他同理):
点击查看代码
#include<bits/stdc++.h> #define N 510 #define int long long using namespace std; int n,t; string s; struct Matrix{ int n,m; bool a[N][N]; Matrix(int na,int ma){n=na,m=ma,memset(a,0,sizeof a);} bool* operator [](int x){return a[x];} Matrix(){memset(a,0,sizeof a);} Matrix operator*(const Matrix &b) const{ Matrix res(n,b.m); for(int i=1;i<=n;i++){ for(int j=1;j<=b.m;j++){ for(int k=1;k<=m;k++){ res[i][j]^=a[i][k]&b.a[k][j]; } } } return res; } }a,base; void qpow(int b){ while(b){ if(b&1) a=a*base; base=base*base; b>>=1; } } signed main(){ cin>>n>>t>>s; a.n=1,a.m=base.n=base.m=n; for(int i=1;i<=n;i++) a[1][i]=s[i-1]-'0'; for(int i=1;i<=n;i++){ int p1=i-1,p2=i+1; if(p1==0) p1+=n; if(p2==n+1) p2-=n; base[i][p1]=base[i][p2]=1; } qpow(t); for(int i=1;i<=n;i++) cout<<a[1][i]; return 0; }
但由于此方法复杂度是\(O(n^3\log T)\),空间是\(O(n^2)\),都无法承受。
100pts - 找规律&倍增 - \(O(n\log T)\)
假设现在有一个无限长的环:
...000000010000000...
进行变化(下面用#
表示\(1\),空格表示\(0\)),发现:
# //初始状态 # # //经过1次变化 # # //经过2次变化 # # # # # # //经过4次变化 # # # # # # # # # # # # # # # # # # //经过8次变化 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # //经过16次变化 # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .............
很神奇地,我们发现这是一个分形图(谢尔宾斯基三角形)。
当然这不是重点,我们发现,经过\(2^k\)次变化后,只有一开始的活细胞左边第\(2^k\)个细胞和右边第\(2^k\)个细胞是活的,其他全是死的。
因此我们可以得知任意一种环上,变化\(2^k\)次后每个活细胞向左右的贡献,即:对于每个活细胞,将其左&右边第\(2^k\)个细胞状态取反,自己也取反。
通过把\(t\)二进制分解,可以转化成若干个\(2^k\)次变化,重复上述操作即可。
时间复杂度\(O(n\log t)\),可以通过。
注意是一个环,所以位置需要取模\(n\)。
#include<bits/stdc++.h> #define N 100010 #define int long long using namespace std; int n,t,a[2][N]; string s; int modn(int a){return ((a%n)+n)%n;} signed main(){ cin>>n>>t>>s; bool cur=0; for(int i=0;i<n;i++) a[0][i]=s[i]-'0'; for(int ii=0,w=1;ii<55;ii++,w<<=1){//k=2^i if(t&w){ cur^=1; for(int i=0;i<n;i++) a[cur][i]=a[cur^1][i]; for(int i=0;i<n;i++) if(a[cur^1][i]) a[cur][i]^=1,a[cur][modn(i-w)]^=1,a[cur][modn(i+w)]^=1; } } for(int i=0;i<n;i++) cout<<a[cur][i]; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效