CodeForces 575A - Fibonotci

集训队作业哦。

粉兔:“矩阵快速幂 sb 题。”

洛谷题目页面传送门 & CF 题目页面传送门

题意见洛谷。

一脸的矩阵,用矩阵把递推式表示出来:

\[\begin{bmatrix}f_i&f_{i+1}\end{bmatrix}\Delta_i=\begin{bmatrix}f_{i+1}&f_{i+2}\end{bmatrix} \]

其中

\[\Delta_i=\begin{bmatrix}0&s_i\\1&s_{i+1}\end{bmatrix} \]

那么答案就是

\[\left(\begin{bmatrix}f_0 & f_1\end{bmatrix}\prod_{i=0}^{k-1}\Delta_i\right)_{1,1}=\left(\prod_{i=0}^{k-1}\Delta_i\right)_{2,1} \]

只需要求那个 \(\prod\) 即可。

注意到 \(s\) 是有周期性的,\(n\) 个一周期。先不考虑有 \(m\) 个特殊点,这样显然可以算出 \(n\) 个的积,然后快速幂。

如果有 \(m\) 个特殊点呢?考虑在有特殊点处分段,有特殊点的周期们特殊算,无特殊点的周期段们快速幂。周期段数显然是 \(\mathrm O(m)\) 的,那么快速幂部分就是 \(\mathrm O(m\log)\)

如何快速维护特殊算部分呢?考虑在原 \(n\) 个矩阵的乘积上修改,注意到一个特殊的 \(s_i\) 只会影响 \(\Delta_i\)\(\Delta_{i-1}\)。但是矩阵乘法没有逆运算,不能直接除或 BIT;需要修改,也不能倍增。可以用线段树维护,单点修改,整体查积,每个有特殊点的周期算完就改回去。

时间复杂度 \(\mathrm O(n+m\log)\)

代码:

#include<bits/stdc++.h>
using namespace std;
#define mp make_pair
#define X first
#define Y second
#define pb push_back
#define int long long
const int inf=0x3f3f3f3f3f3f3f3f;
const int N=50000,M=50000;
int k,mod;
int n;
int s[N];
int m;
pair<int,int> v[M+1];
vector<pair<int,pair<bool,int> > > v0;
struct matrix{//矩阵 
	int a[2][2];
	int *operator[](int x){return a[x];}
	matrix(int x=0,int y=0,int z=0,int xx=0){
		a[0][0]=x,a[0][1]=y,a[1][0]=z,a[1][1]=xx;
	}
	friend matrix operator*(matrix x,matrix y){
		return matrix((x[0][0]*y[0][0]+x[0][1]*y[1][0])%mod,
		              (x[0][0]*y[0][1]+x[0][1]*y[1][1])%mod,
		              (x[1][0]*y[0][0]+x[1][1]*y[1][0])%mod,
		              (x[1][0]*y[0][1]+x[1][1]*y[1][1])%mod);
	}
	void prt(){printf("%lld %lld\n%lld %lld\n",a[0][0],a[0][1],a[1][0],a[1][1]);}
};
matrix qpow(matrix x,int y){//快速幂 
	matrix res(1,0,0,1);
	while(y){
		if(y&1)res=res*x;
		x=x*x;
		y>>=1;
	}
	return res;
}
struct segtree{//线段树 
	struct node{int l,r;matrix v;}nd[N<<2];
	#define l(p) nd[p].l
	#define r(p) nd[p].r
	#define v(p) nd[p].v
	void sprup(int p){v(p)=v(p<<1)*v(p<<1|1);}
	void bld(int l=0,int r=n-1,int p=1){
		l(p)=l;r(p)=r;
		if(l==r)return v(p)=matrix(0,s[l],1,s[(l+1)%n]),void();
		int mid=l+r>>1;
		bld(l,mid,p<<1);bld(mid+1,r,p<<1|1);
		sprup(p);
	}
	void init(){bld();}
	void chg(int x,bool y,int v,int p=1){
		if(l(p)==r(p))return v(p)[y][1]=v,void();
		int mid=l(p)+r(p)>>1;
		chg(x,y,v,p<<1|(x>mid));
		sprup(p);
	}
	matrix prod(int l=0,int r=n-1,int p=1){
		if(l>r)return matrix(1,0,0,1);
		if(l<=l(p)&&r>=r(p))return v(p);
		int mid=l(p)+r(p)>>1;
		matrix res(1,0,0,1);
		if(l<=mid)res=res*prod(l,r,p<<1);
		if(r>mid)res=res*prod(l,r,p<<1|1);
		return res;
	}
}segt;
signed main(){
	cin>>k>>mod;
	cin>>n;
	for(int i=0;i<n;i++)cin>>s[i];
	cin>>m;
	for(int i=1;i<=m;i++)cin>>v[i].X>>v[i].Y;
	v0.pb(mp(-n,mp(0,0)));v0.pb(mp(inf,mp(0,0)));
	for(int i=1;i<=m;i++){
		v0.pb(mp(v[i].X,mp(0,v[i].Y)));
		if(v[i].X)v0.pb(mp(v[i].X-1,mp(1,v[i].Y)));
	}
	sort(v0.begin(),v0.end());
	segt.init();
	matrix res(1,0,0,1);
	int las=0;
	for(int i=1,ie;i<v0.size();i=ie+1){
		ie=i;while(ie+1<v0.size()&&v0[ie+1].X/n==v0[i].X/n)ie++;
		if(v0[i].X/n>=k/n){las=v0[i-1].X/n;break;}
		res=res*qpow(segt.prod(),v0[i].X/n-1-v0[i-1].X/n);
		for(int j=i;j<=ie;j++)segt.chg(v0[j].X%n,v0[j].Y.X,v0[j].Y.Y);//修改 
		res=res*segt.prod();
		for(int j=i;j<=ie;j++)segt.chg(v0[j].X%n,v0[j].Y.X,v0[j].Y.X?s[(v0[j].X+1)%n]:s[v0[j].X%n]);//改回去 
	}
	res=res*qpow(segt.prod(),k/n-1-las);
	for(int i=0;i<v0.size();i++)if(v0[i].X/n==k/n)segt.chg(v0[i].X%n,v0[i].Y.X,v0[i].Y.Y);
	res=res*segt.prod(0,k%n-1);
	cout<<res[1][0];
	return 0;
}
posted @ 2020-08-22 16:15  ycx060617  阅读(121)  评论(0编辑  收藏  举报