把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【CF575A】Fibonotci(矩乘套路题)

点此看题面

  • 给定一个长度为\(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;//输出答案
}
posted @ 2020-11-22 15:23  TheLostWeak  阅读(120)  评论(0编辑  收藏  举报