[NOI2013]矩阵游戏

[NOI2013]矩阵游戏


此题解法大约两种,矩阵乘法和推式子

都说一下

矩阵乘法

构造一下矩阵

\[\begin{bmatrix} F[i][j] & 1 \\ 0 & 0 \end{bmatrix} \times \begin{bmatrix} a & 0 \\ b & 1 \end{bmatrix} = \begin{bmatrix} F[i][j+1] & 1 \\ 0 & 0 \end{bmatrix} \]

\[\begin{bmatrix} F[i][m] & 1 \\ 0 & 0 \end{bmatrix} \times \begin{bmatrix} c & 0 \\ d & 1 \end{bmatrix} = \begin{bmatrix} F[i+1][1] & 1 \\ 0 & 0 \end{bmatrix} \]

\(\begin{bmatrix}a & 0 \\b & 1\end{bmatrix}\)称为\(A\),\(\begin{bmatrix}c & 0 \\d & 1\end{bmatrix}\)称作\(B\)

答案就是\(\begin{bmatrix}1 & 1 \\0 & 0\end{bmatrix}\times A^{n(m-1)}\times B^{n-1}\)这个矩阵的左上角元素

搭配十进制快速幂可获得80~100不等的分数

然后一些dalao发现元素会有循环,若a=1,循环节为1e9+7,否则为1e9+6

循环节在每行,每列都会循环,所以n,m取模即可

妥妥的AC

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
#define il inline
#define rg register
#define vd void
#define sta static
#define int long long
#define mod 1000000007
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
struct yyb{
    int a,b,c,d;
    yyb(int _a=1,int _b=0,int _c=0,int _d=1){a=_a%mod,b=_b%mod,c=_c%mod,d=_d%mod;}
};
yyb operator *(const yyb&a,const yyb&b){
    return (yyb){
        a.a*b.a+a.b*b.c, a.a*b.b+a.b*b.d,
        a.c*b.a+a.d*b.c, a.c*b.b+a.d*b.d
    };
}
char N[1000100],M[1000100];
int a,b,c,d,lenn,lenm,n,m;
yyb operator ^(yyb a,int b){
    yyb ret;
    while(b){
        if(b&1)ret=ret*a;
        a=a*a;b>>=1;
    }
    return ret;
}
main(){
    scanf("%s%s%lld%lld%lld%lld",N+1,M+1,&a,&b,&c,&d);
    lenn=strlen(N+1),lenm=strlen(M+1);
    if(a!=1){
        for(rg int i=1;N[i];++i)n=(n*10+N[i]-'0')%(mod-1);
        for(rg int i=1;M[i];++i)m=(m*10+M[i]-'0')%(mod-1);
        if(!n)n=mod-1;if(!m)m=mod-1;
    }else{
        for(rg int i=1;N[i];++i)n=(n*10+N[i]-'0')%mod;
        for(rg int i=1;M[i];++i)m=(m*10+M[i]-'0')%mod;
        if(!n)n=mod;if(!m)m=mod;
    }
    yyb S=(yyb){1,1,0,0},A=(yyb){a,0,b,1},B=(yyb){c,0,d,1};
    A=(((A^(m-1))*B)^(n-1))*(A^(m-1));
    printf("%lld\n",(S*A).a);
    return 0;
}

暴力推式子

考虑从\(F[i][1]\)\(F[i+1][1]\)的变化。

这个和随稽数生成器那题有点像(那道题保证互质就能用这个方法AC了)

\(a \ne 1:\)

\(F[i][m]=a^{m-1}F[i][1]+b(a^0+a^1+\cdot\cdot\cdot+a^{m-2})=a^{m-1}F[i][1]+b\times\frac{a^{m-1}}{a-1}\)

\(F[i+1][1]=a^{m-1}\times c\times F[i][1]+b\times c\times\frac{a^{m-1}}{a-1}+d\)

\(p=a^{m-1}\times c,q=b\times c\times\frac{a^{m-1}}{a-1}+d\)

可以发现\(F[i+1][1]=p\times F[i][1]+q\)

\(a = 1:\)

\(F[i][m]=F[i][1]+b(m-1)\)

\(F[i+1][1]=c\times F[i][1]+b(m-1)\times c+d\)

这时的\(p=c,q=b(m-1)\times c+d\)

再按上述方法推一次可算出\(F[n+1][1]\)

减d除c即可得到答案

n和m好像也可以按矩阵的方法取模。。然后蜜汁85

贴上吧:

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#define il inline
#define rg register
#define vd void
#define sta static
#define int long long
#define mod 1000000007
il int gi(){
	rg int x=0,f=1;rg char ch=getchar();
	while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
	while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
il int pow(int x,int y){
	int ret=1;
	while(y){
		if(y&1)ret=ret*x%mod;
		x=x*x%mod;y>>=1;
	}
	return ret;
}
char nn[1000001],mm[1000001];
int n,m,a,b,c,d;
main(){
	freopen("matrix.in","r",stdin);
	freopen("matrix.out","w",stdout);
	scanf("%s%s",nn+1,mm+1);
	for(rg int i=1;nn[i];++i)n=(n*10+nn[i]-'0')%(mod-1);
	for(rg int i=1;mm[i];++i)m=(m*10+mm[i]-'0')%(mod-1);
	a=gi(),b=gi(),c=gi(),d=gi();
	int A,B,ans;
	if(a==1)A=c,B=(b*c%mod*(m-1)%mod+d)%mod;
	else{
		int p=pow(a,m-1);
		A=p*c%mod,B=(b*c%mod*(p-1)%mod*pow(a-1,mod-2)%mod+d)%mod;		
	}
	if(A==1)ans=(1+B*n)%mod;
	else{
		int p=pow(A,n);
		ans=(p+B*(p-1)%mod*pow(A-1,mod-2)%mod)%mod;
	}
	printf("%lld\n",(ans-d+mod)%mod*pow(c,mod-2)%mod);
	return 0;
}
posted @ 2018-02-26 13:30  菜狗xzz  阅读(265)  评论(0编辑  收藏  举报