bzoj 2326: [HNOI2011]数学作业(矩阵快速幂)

本文介绍了一道HNOI2011竞赛题“数学作业”的算法解决方案,通过矩阵快速幂的方法实现了O(logn)的时间复杂度。文章提供了完整的C语言代码实现,并详细解释了如何将题目中的数列转换为矩阵运算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2326: [HNOI2011]数学作业

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 2249   Solved: 1301
[ Submit][ Status][ Discuss]

Description


令F(n) = Concatenate(1, n)

那么有

F(x) = F(x-1)*10+(x-1)+1     x∈(1, 9)

F(x) = F(x-1)*100+(x-1)+1     x∈(10, 99)

F(x) = F(x-1)*1000+(x-1)+1     x∈(100, 999)

F(x) = F(x-1)*10000+(x-1)+1     x∈(1000, 9990)

……

其中最多18段(题目中没有n=10^18这组数据)

每一段的公式都可以转成对应的矩阵快速幂

这样复杂度就是O(logn)了

矩阵方程:



#include<stdio.h>
#include<string.h>
#define LL long long
typedef struct
{
	LL a[4][4];
	void init(LL x)
	{
		memset(a, 0, sizeof(a));
		a[1][1] = x;
		a[2][1] = a[2][2] = a[3][1] = a[3][2] = a[3][3] = 1;
	}
	void unit()
	{
		memset(a, 0, sizeof(a));
		a[1][1] = a[2][2] = a[3][3] = 1;
	}
}Matrix;
Matrix Jz;
LL ans[4], temp[4];
Matrix Mult(Matrix A, Matrix B, LL mod)
{
	LL i, j, k;
	Matrix C;
	memset(C.a, 0, sizeof(C.a));
	for(i=1;i<=3;i++)
	{
		for(j=1;j<=3;j++)
		{
			for(k=1;k<=3;k++)
				C.a[i][j] = (C.a[i][j]+A.a[i][k]*B.a[k][j])%mod;
		}
	}
	return C;
}
Matrix Pow(Matrix A, LL b, LL mod)
{
	Matrix C;
	C.unit();
	while(b)
	{
		if(b%2)
			C = Mult(C, A, mod);
		A = Mult(A, A, mod);
		b /= 2;
	}
	return C;
}
int main(void)
{
	LL n, m, i, j, k, flag;
	while(scanf("%lld%lld", &n, &m)!=EOF)
	{
		ans[1] = ans[2] = 0;
		ans[3] = 1;
		flag = 0;
		if(n==(LL)1e18-1)
			flag = 1, n--;
		for(i=9;i<=n&&i>=9;i=i*10+9)
		{
			Jz.init((i+1)%m);
			Jz = Pow(Jz, (i+1)/10*9, m);
			temp[1] = ans[1], temp[2] = ans[2], temp[3] = ans[3];
			ans[1] = ans[2] = ans[3] = 0;
			for(j=1;j<=3;j++)
			{
				for(k=1;k<=3;k++)
					ans[j] = (ans[j]+temp[k]*Jz.a[k][j])%m;
			}
		}
		Jz.init((i+1)%m);
		Jz = Pow(Jz, n-(i-9)/10, m);
		temp[1] = ans[1], temp[2] = ans[2], temp[3] = ans[3];
		ans[1] = ans[2] = ans[3] = 0;
		for(j=1;j<=3;j++)
		{
			for(k=1;k<=3;k++)
				ans[j] = (ans[j]+temp[k]*Jz.a[k][j])%m;
		}
		if(flag==1)
			ans[1] = (ans[1]*((LL)1e18%m)+((LL)1e18-1)%m)%m;
		printf("%lld\n", ans[1]%m);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值