ABC129F题解

ABC129F Takahashi’s Basics in Education and Learning

题目大意

有一个等差数列,有 L L L个元素: s 0 , s 1 , s 2 , … , s L − 1 s_0,s_1,s_2,\dots,s_{L-1} s0,s1,s2,,sL1.初始元素为 A A A,公差为 B B B s i = A + B × i s_i=A+B\times i si=A+B×i.将这些元素从左到右拼接为一个整数,如 3 , 7 , 11 , 15 , 19 3,7,11,15,19 3,7,11,15,19拼接起来得到 37111519 37111519 37111519这个整数。输出拼接后的整数模 M M M的结果,数据保证等差数列中的前 L L L项都不超过 1 0 18 10^{18} 1018.


题解

当存在一对整数 ( l , r ) (l,r) (l,r),满足 ∀ i ∈ [ l , r ] , 1 0 d − 1 ≤ s i < 1 0 d \forall i\in[l,r],10^{d-1}\leq s_i< 10^d i[l,r],10d1si<10d,设拼接到第 i − 1 i-1 i1个整数时这个数为 x x x,则拼接到第 i i i个整数时这个数为 x + s i x+s_i x+si。我们构造一个向量 ( x , s i ) (x,s_i) (x,si)和一个矩阵 C C C,使得 ( x , s i ) × C = ( x × 1 0 d , s i + B ) (x,s_i)\times C=(x\times10^d,s_i+B) (x,si)×C=(x×10d,si+B),其中 s i + B s_i+B si+B就是 s i + 1 s_{i+1} si+1

但在构造矩阵的时候,我们发现难以将 s i s_i si变为 s i + B s_i+B si+B,所以我们将向量变为 ( x , s i , B ) (x,s_i,B) (x,si,B),那么:

( x , s i , B ) × [ 1 0 d 0 0 1 1 0 0 1 1 ] = ( x × 1 0 d + s i , s i + B , B ) (x,s_i,B)\times \left[\begin{matrix}10^d & 0 & 0 \\1 & 1 & 0 \\0 & 1 & 1\end{matrix}\right]=(x\times10^d+s_i,s_i+B,B) (x,si,B)×10d10011001=(x×10d+si,si+B,B)

那么,用矩阵快速幂就能求出对于每个 d d d的答案了。

那么,怎么求对于每个 d d d,矩阵要乘的次数呢?

设当前这一段每个数都有 d d d位的范围为 ( l d , r d ) (l_d,r_d) (ld,rd),对于每个 d d d,显然 i = l d i=l_d i=ld是满足 A + B × i ≥ 1 0 d A+B\times i\geq10^d A+B×i10d的最小值。变形之后就变为 i ≥ 1 0 d − A B i\geq\dfrac{10^d-A}{B} iB10dA。那么 l d = ⌈ 1 0 d − A B ⌉ l_d=\lceil\dfrac{10^d-A}{B}\rceil ld=B10dA。当然,有时候 ⌈ 1 0 d − A B ⌉ \lceil\dfrac{10^d-A}{B}\rceil B10dA为负,所以得出的 l d l_d ld要与 0 0 0 max ⁡ \max max。显然,此时的 r d = l d + 1 − 1 r_d=l_{d+1}-1 rd=ld+11

时间复杂度为 O ( 18 × 27 log ⁡ L ) O(18\times27\log L) O(18×27logL)

code

#include<bits/stdc++.h>
using namespace std;
long long l,a,b,p,ans,mi[25],h[25];
struct matrix{
	long long a[4][4];
	matrix operator*(const matrix ax)const{
		matrix cx;
		memset(cx.a,0,sizeof(cx.a));
		for(int i=1;i<=3;i++){
			for(int j=1;j<=3;j++){
				for(int k=1;k<=3;k++){
					cx.a[i][j]=(cx.a[i][j]%p+a[i][k]%p*(ax.a[k][j]%p)%p)%p;
				}
			}
		}
		return cx;
	}
}re,w;
matrix ksm(matrix t,long long v){
	matrix c;
	memset(c.a,0,sizeof(c.a));
	if(v==0){
		c.a[1][1]=c.a[2][2]=c.a[3][3]=1;
		return c;
	}
	c=ksm(t,v/2);
	c=c*c;
	if(v%2) c=c*t;
	return c;
}
int main()
{
	scanf("%lld%lld%lld%lld",&l,&a,&b,&p);
	memset(re.a,0,sizeof(re.a));
	memset(w.a,0,sizeof(w.a));
	mi[0]=1;
	for(int i=1;i<=18;i++) mi[i]=mi[i-1]*10;
	for(int i=1;i<=18;i++){
		h[i]=(mi[i]-a)/b;
		if((mi[i]-a)%b>0) ++h[i];
		h[i]=max(h[i],0ll); 
	}
	w.a[2][1]=w.a[2][2]=w.a[3][3]=1;
	w.a[3][2]=1;
	re.a[1][2]=a;re.a[1][3]=b;
	for(int i=0;i<=17;i++){
		w.a[1][1]=mi[i+1];
		re=re*ksm(w,min(h[i+1]-h[i],l));
		l-=h[i+1]-h[i];
		if(l<=0) break;
	}
	printf("%lld",re.a[1][1]);
	return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值