UVA1386 【Cellular Automaton】题解
題面:UVA1386 Cellular Automaton
矩陣乘法+快速冪解法:
這是一個比較裸的有點復雜需要優化的矩乘快速冪,所以推薦大家先做一下下列洛谷題目練練手:
(會了,差不多就是多倍經驗題了)
注:如果你還不會矩陣乘法,可以移步了解一下P3390的題解
P1939 【模板】矩陣加速(數列)
P3390 【模板】矩陣快速冪
P1962 斐波那契數列
P4910 帕秋莉的手環
P4838 P哥破解密碼
然后講一下本題,讀題我們發現這個環上所進行的 k 次操作都是一模一樣的,還是相鄰的數的和,于是想到用矩陣快速冪來寫。
解題思路:
我們可以根據 d 的值來建出基礎矩陣,舉個例子(樣例):
1 1 0 0 11 1 1 0 00 1 1 1 00 0 1 1 11 0 0 1 1這是樣例每一次操作所對應的乘法矩陣。我們要求出 k 次操作后的環上的元素等同于乘以 k 次這個矩陣,而這個過程可以直接用快速冪優化成log復雜度。
于是我們便可以光明正大的提交,然后光榮 TLE 了
為什么呢?我們看到數據范圍:$ n \leq 500$ !眾所周知:矩陣乘法中矩陣上每一個元素都會被用行乘列的方式更新,所以復雜度為 \(n^3\),在乘上快速冪的一個log n ,不TLE才怪!
優化方案:
于是我們嘗試優化一下(把那個\(n^3\)降到\(n^2\)):我們再看到上面樣例的矩陣,可以發現它的下一行等于上一行所有的 1 向右移一位。所以我們進行矩陣乘法時可以不更新矩陣的每一個位置,而是只更新它的第一行(將它壓縮一下)其它幾行可以通過第一行推出來。
例如:
0 2 1 1 2我們可以通過這一行推出整個矩陣(向右移一位):
0 2 1 1 2 2 0 2 1 1 1 2 0 2 1 1 1 2 0 2 2 1 1 2 0于是乎,我們寫一個新的”矩陣乘法“代碼如下:
void cheng(ll a[],ll b[]){for(int i=1;i<=n;i++)//預處理 1res[i]=0,s[1][i]=b[i];for(int i=2;i<=n;i++){s[i][1]=s[i-1][n];for(int j=2;j<=n;j++)s[i][j]=s[i-1][j-1];}// 解壓一維矩陣,復雜度不會變回 n^3!for(int i=1;i<=n;i++)for(int k=1;k<=n;k++)res[i]+=(a[k]*s[i][k])%m,res[i]%=m;for(int i=1;i<=n;i++)a[i]=res[i]; }// 本題專用“矩陣乘法” //請不要直接給a數組賦值,不然后果自負!然后在用快速冪優化一下:
while(k){if(k&1)cheng(ans,base);cheng(base,base);k>>=1;}完整代碼:
#include<iostream> #include<cstdio> #include<iomanip> #include<algorithm>#define ll long long #define db double #define inf 0x7fffffffusing namespace std;const int l=501; int n,m,d,k; ll s[l][l]; ll ans[l],base[l],res[l]; // ans=(answer),base(基礎矩陣),res=(result) // s數組是用來解壓矩陣的(將一維矩陣換成二維)inline ll qr(){char ch;while((ch=getchar())<'0'||ch>'9');ll res=ch^48;while((ch=getchar())>='0'&&ch<='9')res=(res<<1)+(res<<3)+(ch^48);return res; }void cheng(ll a[],ll b[]){for(int i=1;i<=n;i++)//預處理 1res[i]=0,s[1][i]=b[i];for(int i=2;i<=n;i++){s[i][1]=s[i-1][n];for(int j=2;j<=n;j++)s[i][j]=s[i-1][j-1];}// 解壓一維矩陣,復雜度不會變回 n^3!for(int i=1;i<=n;i++)for(int k=1;k<=n;k++)res[i]+=(a[k]*s[i][k])%m,res[i]%=m;for(int i=1;i<=n;i++)a[i]=res[i]; }// 本題專用“矩陣乘法” //請不要直接給a數組賦值,不然后果自負!int main(){while(scanf("%d%d%d%d",&n,&m,&d,&k)!=EOF){for(int i=1;i<=n;i++)//預處理 2ans[i]=qr(),base[i]=0;for(int i=0;i<=d;i++)//單位矩陣base[1+i]=base[(n-i)%n+1]=1;while(k){if(k&1)cheng(ans,base);cheng(base,base);k>>=1;}// 只有四行的快速冪for(int i=1;i<n;++i){printf("%lld ",ans[i]);}printf("%lld\n",ans[n]);}return 0; }//預處理 1:將res數組歸零,將b數組賦值給s數組第一維以便壓縮//預處理 2:給ans數組賦初值,將單位矩陣清零注:解壓矩陣會消耗很多時間,也可以不解壓,但乘起來會有些麻煩(蒟蒻手殘,就不壓行了吧)。
轉載于:https://www.cnblogs.com/812-xiao-wen/p/9871484.html
總結
以上是生活随笔為你收集整理的UVA1386 【Cellular Automaton】题解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 触发器介绍
- 下一篇: 008 python接口 unittes