• 【CF575A】Fibonotci(矩乘套路题)


    点此看题面

    • 给定一个长度为(n)的数组(s),从(0)开始编号。
    • 规定当(ige n)时,除去(m)个直接给定的(s_i),其余(s_i)一律等于(s_{i mod n})
    • 已知(f_0=0,f_1=1,forall ige2,f_i=s_{i-2}f_{i-2}+s_{i-1}f_{i-1}),求(s_k)
    • (n,mle5 imes10^4,kle10^{18})

    (m=0)时直接上矩乘

    假设(m=0),则第(i)个位置的转移矩阵就是:

    [egin{bmatrix} 0&s_{(i-2) mod n}\ 1&s_{(i-1) mod n} end{bmatrix} ]

    那么就是要求第(2sim 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
    ",t[1][1]),0;//输出答案
    }
    
  • 相关阅读:
    CKA易错题整理:五、配置⽹络策略 NetworkPolicy
    CKA易错题整理:第四题:备份还原 etcd
    2022 暑期复健训练(持续更新)
    【其他】STL 容器初始化后占用空间大小测试
    2步 从百度网盘好友分享文件中导出文件目录
    简单易懂的时序数据压缩算法分析
    判断字符串是否可以转成BigDecimal(可转用小数,负数)
    RocketMq
    【leetCode】三数之和
    【leetCode】盛最多水的容器
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF575A.html
Copyright © 2020-2023  润新知