WOJ 654 递推+矩阵快速幂

654. A simple math problem

Time Limit: 1000 ms / Mem. Limit: 524288 KiB / IO: stdio


Given a number nnn, you should calculate 123456…11121314…n123456\ldots 11121314\ldots n12345611121314n module 11

Input

A single line with an integer nnn (0<n≤10180 < n \le 10^{18}0<n1018)

Output

Output one integer, 123456…11121314…n123456\ldots 11121314\ldots n12345611121314n module 11

Examples

Input 1

1

Output 1

1

Input 2

20

Output 2

5

Input 3

21

Output 3

4

Note

1≡1(mod11)1 \equiv 1 (\mod 11)11(mod11)

1234567891011121314151617181920≡5(mod11)1234567891011121314151617181920 \equiv 5 (\mod 11)12345678910111213141516171819205(mod11)

123456789101112131415161718192021≡4(mod11)123456789101112131415161718192021 \equiv 4 (\mod 11)1234567891011121314151617181920214(mod11).

Source

第六届华中区程序设计邀请赛暨武汉大学第十五届校赛


         此题就是之前所说的,矩阵快速幂递推啦。我就真的很好奇,为什么当时想不到用矩阵快速幂……

        题目,很容易理解,输入一个n,把从1开始到n的所有数字连成一个数字,然后问这个数字模11的结果是什么。

        首先,显然会想到n的结果就是n-1的结果乘上n的位数加上n再模11。O(n)的算法,但是仍然会超时。于是我就开始了漫长的找规律之旅,然后发现这是个无底洞……自己手残经常敲错东西,然后有时又忘了改一些东西。然后中午没有吃东西,脑子一片混乱,总之耗了一个多小时还没有弄出来。不,是WA了两次,然后放弃了。

        现在想来,把我的思想总结一下就是f(n)=(f(n-1)*10^p+n)%11。其中p表示n的位数。我想,如果我把这个式子写出来或许我就可以想到矩阵快速幂了,然而……那么我们就来说说矩阵对递推的式的优化吧。对于任何线性的递推,我们都可以把递推关系写在矩阵里面,即可以把通项公式写成an=a0*matrix^n,这样子的话,我们就可以利用矩阵乘法优化这个递推的过程。

        这题的话由于p的不同,所以我们要分成18个矩阵并分成一段一段地区求。构造方式的话设递推矩阵为x,x={{10^p,0,0}{1,1,0}{1,1,1}},然后的话首项是an={{an,n,1}{0,0,0}{0,0,0}},这样子刚好就可以完成递推,不信自己去试一试。关于构造这个矩阵,我记得我高中高OI的时候好像读过一篇文章专门讲如果构造递推矩阵,然而现在不记得了……下次找到了再讲吧。

  还有,值得注意到是,对于求位数p,本想装个B用log换底直接求位数,但是由于一些精度问题老师错……最后还是妥协了……代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iomanip>
#define ULL unsigned long long
#define mod 11
#define N 4
using namespace std;

struct matrix
{
	ULL a[N][N];
} x,s,basic;

ULL n,y;

matrix matmulti(matrix x,matrix y)
{
	matrix ans;
	for(int i=1;i<N;i++)
		for(int j=1;j<N;j++)
		{
		    ans.a[i][j]=0;
	    	for(int k=1;k<N;k++)
	    	{
	    	    ans.a[i][j]+=(x.a[i][k]*y.a[k][j])%mod;
	    	    ans.a[i][j]%=mod;
	    	}
		}
	return ans;
}

matrix matpow(matrix x,ULL y)
{
	if (y==0) return basic;
	else 
		while ((y&1)==0)
		{
			y>>=1;
			x=matmulti(x,x);
		}
	matrix ans=x;
	
	y>>=1;
	while (y!=0)
	{
		x=matmulti(x,x);
		if ((y&1)!=0) ans=matmulti(ans,x);
		y>>=1;	
	}
	return ans;	
}

int main()
{
	s.a[1][1]=0; 
	x.a[2][1]=x.a[2][2]=1;
	s.a[1][2]=0;s.a[1][3]=1;
	x.a[3][1]=x.a[3][2]=x.a[3][3]=1;
	for(int i=1;i<=3;i++) basic.a[i][i]=1;
	scanf("%lld",&n);
	ULL nn=n,p=0;
	while (nn>0)
	{
		nn/=10; p++;
	}
	ULL i=10,j;
	for(i=10,j=1;j<p;i*=10,j++)
	{
		x.a[1][1]=i%mod;
		s=matmulti(s,matpow(x,9*i/10));
	}
	x.a[1][1]=i%mod;
	s=matmulti(s,matpow(x,n-i/10+1));
	printf("%lld",s.a[1][1]);
}  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值