CF575A Fibonotci
题意
给定一个无限的序列 \(s\),周期为 \(n\),并给定 \(s_{0\sim n-1}\)。在给定 \(m\) 个位置修改 \(s\) 的值。
对于一个 \(f\),有 \(f_i=s_{i-1}f_{i-1}+s_{i-2}f_{i-2}\),求 \(f_k\mod p\)。
Solution
由于 \(k\) 比较大,所以一眼考虑快速幂。很快可以推出转移矩阵:
\[\begin{bmatrix}
f_{i}\\f_{i-1}
\end{bmatrix}=
\begin{bmatrix}
s_{i-1}&s_{i-2}\\
1&0
\end{bmatrix}
\begin{bmatrix}
f_{i-1}\\f_{i-2}
\end{bmatrix}
\]
很容易想到先把一段周期的矩阵乘起来再快速幂。
然后考虑有 \(m\) 个位置需要修改,所以对 \(n\) 个位置建一棵线段树,每次特殊处理这些被修改过的区间即可。
感觉细节挺多的。首先是如果这个修改在边界处,则两段都要改。所以较好的实现姿势是把一次修改拆成修改两个矩阵。
完蛋了,我 tm 学的左乘,导致我所有的乘法运算都要倒过来做,所幸这题并不是十分麻烦。
Code
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<' '<<a<<' '
#define pts(a) cerr<<#a<<' '<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=5e4+10;
const int SIZ=2;
int MOD;
struct Mat{
int a[SIZ][SIZ];
Mat(){}
Mat(int s1,int s2){
a[0][0]=s1;a[0][1]=s2;
a[1][0]=1;a[1][1]=0;
}
Mat friend operator*(Mat m1,Mat m2){
Mat ret;memset(ret.a,0,sizeof(ret.a));
rep(i,0,SIZ-1) rep(k,0,SIZ-1) if(m1.a[i][k])
rep(j,0,SIZ-1) (ret.a[i][j]+=m1.a[i][k]*m2.a[k][j]%MOD)%=MOD;
return ret;
}
void print(){
cerr<<a[0][0]<<' '<<a[0][1]<<'\n';
cerr<<a[1][0]<<' '<<a[1][1]<<'\n';
cerr<<"------------\n";
}
};
Mat ksm(Mat m,int p){
Mat ret;memset(ret.a,0,sizeof(ret.a));
ret.a[0][0]=ret.a[1][1]=1;
while(p){
if(p&1) ret=ret*m;
m=m*m; p>>=1;
}return ret;
}
struct Tree{int l,r;Mat info;}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
int s[MAXN],n;
void pushup(int i){
tr[i].info=tr[rs].info*tr[ls].info;
}
void build(int i,int l,int r){
tr[i].l=l;tr[i].r=r;
if(l==r){tr[i].info=Mat(s[l%n],s[(l-1)%n]);return;}
int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);
pushup(i);
}
void upd(int i,int x,Mat v){
if(tr[i].l==tr[i].r){tr[i].info=v;return;}
int mid=(tr[i].l+tr[i].r)>>1;
if(x<=mid) upd(ls,x,v);else upd(rs,x,v);
pushup(i);
}
Mat ask(int i,int l,int r){
if(tr[i].l==l&&tr[i].r==r) return tr[i].info;
int mid=(tr[i].l+tr[i].r)>>1;
if(r<=mid) return ask(ls,l,r);else if(l>mid) return ask(rs,l,r);
else return ask(rs,mid+1,r)*ask(ls,l,mid);
}
struct upds{
int j,v;
void input(){cin>>j>>v;}
bool friend operator<(upds a,upds b){return a.j<b.j;}
}u[MAXN];
struct Upds{
int j;Mat v;
}U[MAXN<<1];
int bl(int id){
return (id-1)/n+1;
}
void solve(){
int k;cin>>k>>MOD;
cin>>n;
rep(i,0,n-1) cin>>s[i];
build(1,1,n);
Mat op;memset(op.a,0,sizeof(op.a));
op.a[0][0]=op.a[1][1]=1;
int m;cin>>m;
rep(i,1,m) u[i].input();
if(k==0){
cout<<0%MOD<<'\n';
return;
}else if(k==1){
cout<<1%MOD<<'\n';
return;
}k--;
sort(u+1,u+1+m);
int mm=0;
rep(i,1,m){
U[++mm].j=u[i].j;
if(u[i-1].j+1==u[i].j) U[mm].v=Mat(u[i].v,u[i-1].v);
else U[mm].v=Mat(u[i].v,s[(u[i].j-1)%n]);
U[++mm].j=u[i].j+1;
if(u[i].j+1==u[i+1].j) U[mm].v=Mat(u[i+1].v,u[i].v);
else U[mm].v=Mat(s[(u[i].j+1)%n],u[i].v);
}
int lst=0,st=-1,ed=-1;
for(int l=1,r;l<=mm;l=r+1){
r=l;while(r+1<=mm&&bl(U[r+1].j)==bl(U[l].j)) r++;
if(bl(U[l].j)==bl(k)){st=l;ed=r;break;}
else if(bl(U[l].j)>bl(k)) break;
op=ksm(tr[1].info,bl(U[l].j)-lst-1)*op;
rep(i,l,r) upd(1,(U[i].j-1)%n+1,U[i].v);
op=tr[1].info*op;lst=bl(U[l].j);
rep(i,l,r) upd(1,(U[i].j-1)%n+1,Mat(s[U[i].j%n],s[(U[i].j-1)%n]));
}
op=ksm(tr[1].info,bl(k)-lst-1)*op;
if(ed!=-1)
rep(i,st,ed) upd(1,(U[i].j-1)%n+1,U[i].v);
op=ask(1,1,(k-1)%n+1)*op;
Mat ans;memset(ans.a,0,sizeof(ans.a));
ans.a[0][0]=1;
ans=op*ans;
cout<<ans.a[0][0]<<'\n';
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// int T;for(cin>>T;T--;)
solve();
return 0;
}