[CF575A]Fibonotci

Fibonotci

题解

不知道哪种 ** 出题人会将这种难调至死的 **题放到 A A A题的位置上。
我相信这种题的做法的做法大家基本上都是很容易想到的,不如说已经很板了。

很明显,我们可以通过矩阵乘法来维护每次转移,就跟普通的,由于它的所有转移式循环的,我们显然只需要维护它 n n n这个循环节以内的矩阵的乘积。
上面矩阵的积显然是可以通过线段树来维护的,这样也同时可以支持我们的对矩阵进行实时修改的。
我们可以对于每个涉及到 s s s值修改的位置的矩阵进行修改,由于点 i i i处的转移矩阵是 { s i − 2 , s i − 1 1 , 0 } \left\{\begin{array}{c}s_{i-2},s_{i-1}\\1,0\end{array}\right\} {si2,si11,0},所以我们修改 s i s_{i} si,要对 i + 1 i+1 i+1 i + 2 i+2 i+2两个位置的矩阵进行修改。
而这个修改还有可能影响的是下一个循环节,还需要特别判断一下。
由于每个循环节的修改都是在最开始的循环节基础上改,我们可以用可持久化线段树来进行维护,这样就不需要刻意去回退了。
对于中间那些不涉及修改的循环节部分我们就可以直接使用矩阵快速幂来进行修改了,直接将最开始的循环节做快速幂乘上去即可。
将每个涉及修改的循环节都枚举一下就行了。

时间复杂度 O ( w 3 ( n + m ( log ⁡   n + log ⁡   K ) ) ) O\left(w^3(n+m(\log\,n+\log\,K))\right) O(w3(n+m(logn+logK)))
实际上 m m m哪里不能说是乘的 l o g   K log\,K logK,因为它相当于是一个分段的,或者说是 ( log ⁡   K − log ⁡   m ) (\log\,K-\log\,m) (logKlogm)会好一点。
反正能过就对了。

源码

看起来有点冗长,主要是判所属循环节有点麻烦~~,调了好久~~。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 50005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;       
const int INF=0x3f3f3f3f;       
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=1e9;
const int n1=400;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(LL x,LL y,LL p){return x+y<p?x+y:x+y-p;}
void Add(LL &x,LL y,LL p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int m,root,nrt;LL k,P,val[MAXN],bp[MAXN],n;
struct ming{LL id,v;}p[MAXN];
bool cmp(ming x,ming y){return x.id<y.id;}
struct matrix{
	LL c[3][3];matrix(){for(int i=1;i<3;i++)for(int j=1;j<3;j++)c[i][j]=0;}
	matrix operator * (const matrix &rhs)const{
		matrix res;
		for(int i=1;i<3;i++)
			for(int k=1;k<3;k++)if(c[i][k])
				for(int j=1;j<3;j++)
					Add(res.c[i][j],1ll*c[i][k]*rhs.c[k][j]%P,P);
		return res;
	}
}A;
matrix qkpow(matrix a,LL s){
	matrix res;res.c[1][1]=res.c[2][2]=1;
	while(s){if(s&1LL)res=res*a;a=a*a;s>>=1LL;}
	return res;
}
struct tann{matrix val;int lson,rson;tann(){val=matrix();lson=rson=0;}};
class SegmentTree{
	private:
		tann tr[MAXN<<3];int tot,lst;
		void pushup(int rt){tr[rt].val=tr[tr[rt].lson].val*tr[tr[rt].rson].val;}
	public:
		void build(int &rt,int l,int r){
			if(!rt)rt=lst=++tot;
			if(l==r){
				tr[rt].val.c[2][1]=1;tr[rt].val.c[2][2]=val[l+1];
				tr[rt].val.c[1][2]=val[l];return ;
			}
			int mid=l+r>>1;
			build(tr[rt].lson,l,mid);build(tr[rt].rson,mid+1,r);
			pushup(rt);
		}
		void insert(int &now,int las,int l,int r,int ai){
			if(l>r||l>ai||r<ai)return ;int mid=l+r>>1;
			if(now==las)tr[now=++tot]=tr[las];
			if(l==r){
				tr[now].val.c[2][1]=1;tr[now].val.c[2][2]=val[l+1];
				tr[now].val.c[1][2]=val[l];return ;
			}
			if(ai<=mid)insert(tr[now].lson,tr[las].lson,l,mid,ai);
			if(ai>mid)insert(tr[now].rson,tr[las].rson,mid+1,r,ai);
			pushup(now);
		}
		matrix query(int rt,int l,int r,int al,int ar){
			if(al<=l&&r<=ar)return tr[rt].val;int mid=l+r>>1;
			if(ar<=mid)return query(tr[rt].lson,l,mid,al,ar);
			if(al>mid)return query(tr[rt].rson,mid+1,r,al,ar);
			return query(tr[rt].lson,l,mid,al,ar)*query(tr[rt].rson,mid+1,r,al,ar); 
		}
		void clear(){tot=lst;}
}T;
signed main(){
	read(k);read(P);read(n);A.c[1][2]=1;
	for(int i=0;i<n;i++)read(val[i]),val[i]%=P;val[n]=val[0];T.build(root,0,n-1);read(m);
	for(int i=1;i<=m;i++){read(p[i].id),read(p[i].v);p[i].v%=P;if(p[i].id>=k)i--,m--;}
	sort(p+1,p+m+1,cmp);LL las=0;
	if(k==0){puts("0");return 0;}
	if(k==1){printf("%d\n",1%P);return 0;}
	for(int i=1,j;i<=m;i=j+1){
		LL now=(p[i].id+n-1LL)/n;j=i;nrt=root;T.clear();
		if(las+1<now)A=A*qkpow(T.query(root,0,n-1,0,n-1),now-las-1LL);
		while(j<m&&(p[j+1].id+n-1LL)/n==now)j++;
		if(i>1&&p[i-1].id==(now-1)*n){bp[0]=val[0];val[0]=p[i-1].v;T.insert(nrt,root,0,n-1,0);}
		for(int l=i;l<=j;l++){
			int t=p[l].id%n;t=t?t:n;
			if(!bp[t])bp[t]=val[t];val[t]=p[l].v;
			T.insert(nrt,root,0,n-1,t-1);
			T.insert(nrt,root,0,n-1,t);
		}
		if((k+n-2LL)/n!=now)A=A*T.query(nrt,0,n-1,0,n-1);
		else A=A*T.query(nrt,0,n-1,0,((k-1LL)%n+n-1LL)%n);
		for(int l=i;l<=j;l++){int t=p[l].id%n;t=t?t:n;if(bp[t])val[t]=bp[t],bp[t]=0;}
		if(bp[0])val[0]=bp[0],bp[0]=0;las=now;
		if(p[j].id==las*n&&(p[j+1].id+n-1LL)/n!=las+1LL&&k!=las*n+1){
			nrt=root;T.clear();bp[0]=val[0];val[0]=p[j].v;T.insert(nrt,root,0,n-1,0);
			if((k+n-2LL)/n!=las+1LL)A=A*T.query(nrt,0,n-1,0,n-1);
			else A=A*T.query(nrt,0,n-1,0,((k-1LL)%n+n-1LL)%n);
			las++;val[0]=bp[0];bp[0]=0;
		}
	}
	LL now=(k+n-2LL)/n;
	if(now^las)if(las+1<now)A=A*qkpow(T.query(root,0,n-1,0,n-1),now-las-1LL);
	if(now^las)A=A*T.query(root,0,n-1,0,((k-1LL)%n+n-1LL)%n);
	printf("%lld\n",A.c[1][2]);
	return 0;
}

谢谢!!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值