【CF575A】Fibonotci(矩乘套路題)
阿新 • • 發佈:2020-11-22
- 給定一個長度為\(n\)的陣列\(s\),從\(0\)開始編號。
- 規定當\(i\ge n\)時,除去\(m\)個直接給定的\(s_i\),其餘\(s_i\)一律等於\(s_{i\ mod\ n}\)。
- 已知\(f_0=0,f_1=1,\forall i\ge2,f_i=s_{i-2}f_{i-2}+s_{i-1}f_{i-1}\),求\(s_k\)。
- \(n,m\le5\times10^4,k\le10^{18}\)
\(m=0\)時直接上矩乘
假設\(m=0\),則第\(i\)個位置的轉移矩陣就是:
\[\begin{bmatrix} 0&s_{(i-2)\ mod\ n}\\ 1&s_{(i-1)\ mod\ n} \end{bmatrix} \]那麼就是要求第\(2\sim k\)個矩陣的總乘積,顯然發現它的週期為\(n\),這種東西隨便做做就好了,相信大家都會,畢竟並不是此題的核心所在。
取關鍵點
考慮一個給定的\(s_i\)影響到的是\(s_{i+1}\)和\(s_{i+2}\),因此我們可以找出\(2m\)個關鍵點。
兩個關鍵點之間的轉移可以按照上面的方式搞,而關鍵點直接特殊造一下矩陣就好了。
這裡可能會涉及求出一段區間矩陣的乘積,講道理矩陣的乘積應該是不能用字首積差分得到的,想想反正已經有一個\(log\)了,乾脆寫個線段樹求一下矩陣區間乘積就好了。
程式碼:\(O(mlogk)\)
#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define N 50000 #define LL long long #define S(x) (U.count(x)?U[x]:s[(x)%n])//求出實際的s[x],也就是判斷其是否為m個特殊點之一 using namespace std; int n,m,X,s[N+5];LL k,q[2*N+5];map<LL,int> U; struct M { int a[2][2];I M(CI A=0,CI B=0,CI C=0,CI D=0) {a[0][0]=A,a[0][1]=B,a[1][0]=C,a[1][1]=D;} I int* operator [] (CI x) {return a[x];}I Con int* operator [] (CI x) Con {return a[x];} I M operator * (Con M& o) Con//矩陣乘法 { M t;t[0][0]=(1LL*a[0][0]*o[0][0]+1LL*a[0][1]*o[1][0])%X; t[0][1]=(1LL*a[0][0]*o[0][1]+1LL*a[0][1]*o[1][1])%X; t[1][0]=(1LL*a[1][0]*o[0][0]+1LL*a[1][1]*o[1][0])%X; t[1][1]=(1LL*a[1][0]*o[0][1]+1LL*a[1][1]*o[1][1])%X;return t; } I M operator ^ (LL y) Con//矩陣快速冪 { M x=*this,t(1,0,0,1);W(y) y&1&&(t=t*x,0),x=x*x,y>>=1;return t; } }; class SegmentTree { private: #define PT CI l=0,CI r=n-1,CI rt=1 #define LT l,mid,rt<<1 #define RT mid+1,r,rt<<1|1 M V[N<<2]; public: I void Build(PT)//建樹 { if(l==r) return (void)(V[rt]=M(0,s[(l-2+n)%n],1,s[(l-1+n)%n])); RI mid=l+r>>1;Build(LT),Build(RT),V[rt]=V[rt<<1]*V[rt<<1|1]; } I M Q(CI L,CI R,PT)//求矩陣區間乘積 { if(L>R) return M(1,0,0,1);if(L==l&&r==R) return V[rt];RI mid=l+r>>1; if(R<=mid) return Q(L,R,LT);if(L>mid) return Q(L,R,RT);return Q(L,mid,LT)*Q(mid+1,R,RT); } }S; int main() { RI i;if(scanf("%lld%d",&k,&X),!k) return puts("0"),0;if(k==1) return puts(X^1?"1":"0"),0;//特判k=0或1 for(scanf("%d",&n),i=0;i^n;++i) scanf("%d",s+i);S.Build(0,n-1);//讀入矩陣預處理 LL x,y;for(scanf("%d",&m),i=1;i<=m;++i) scanf("%lld%lld",&x,&y),U[x]=y,q[i]=x+1,q[m+i]=x+2;//記下2m個關鍵點 q[2*m+1]=k,sort(q+1,q+2*m+2),m=unique(q+1,q+2*m+2)-q-1;W(m&&q[m]>k) --m;//強制加入k成為最後一個關鍵點 LL nx,ny;M t;t[1][1]=1;for(x=0,y=1,i=1;i<=m;x=nx,y=ny,++i) nx=q[i]/n,ny=q[i]%n,y>=ny&&(++x,t=t*S.Q(y+1,n-1)*S.Q(0,ny-1),y=ny-1),//強制y≤ny-1 t=t*((S.Q(y+1,n-1)*S.Q(0,y))^(nx-x))*S.Q(y+1,ny-1)*M(0,S(q[i]-2),1,S(q[i]-1));//中間長度為n的週期直接快速冪一起跳,最後特殊構造關鍵點的矩陣 return printf("%d\n",t[1][1]),0;//輸出答案 }