[luogu P3216] [HNOI2011]数学作业

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[luogu P3216] [HNOI2011]数学作业相关的知识,希望对你有一定的参考价值。

[luogu P3216] [HNOI2011]数学作业

题目描述

小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题:

给定正整数 N 和 M,要求计算 Concatenate (1 .. N) Mod M 的值,其中 Concatenate (1 ..N)是将所有正整数 1, 2, …, N 顺序连接起来得到的数。例如,N = 13, Concatenate (1 .. N)=12345678910111213.小C 想了大半天终于意识到这是一道不可能手算出来的题目,于是他只好向你求助,希望你能编写一个程序帮他解决这个问题。

输入输出格式

输入格式:

从文件input.txt中读入数据,输入文件只有一行且为用空格隔开的两个正整数N和M,其中30%的数据满足1≤N≤1000000;100%的数据满足1≤N≤1018且1≤M≤109.

输出格式:

输出文件 output.txt 仅包含一个非负整数,表示 Concatenate (1 .. N) Mod M 的值。

输入输出样例

输入样例#1:
13 13
输出样例#1:
4

 

很显然,我们可以得到一个递推式:fn=10^len(n)*fn-1+n。然后看到范围那么大,而式子很简单,所以我们就将原式转为矩阵。

显然,根据原式,可以构造出如下矩阵:

{{fn}               {{10^len(n),1,1}                        {{fn-1}

 {n}       =         {0,1,1}                    *                 {n-1}

 {1}}                 {0,0,1}                                      {1}}

但是,我们发现,10^len(n)并不是一个常数。

怎么办?分段来求。1~9一段,10~99一段,100~999一段……10^k~n一段。

这样,就可以避免这一项会变化的情况了。

看起来非常的easy,是不是?写起来就不会这么觉得了。

特别是边界和细节,非常难以处理。调了整整2个多小时。。

code:

技术分享
 1 #include<bits/stdc++.h>
 2 #define LL unsigned long long
 3 using namespace std;
 4 LL n,m,po[20],lim[20];
 5 struct Mat {
 6     LL a[3][3]; Mat() {memset(a,0,sizeof a);}
 7 }tran,ans;
 8 void pre() {
 9     tran.a[0][1]=1,tran.a[0][2]=1;
10     tran.a[1][0]=0,tran.a[1][1]=1,tran.a[1][2]=1;
11     tran.a[2][0]=0,tran.a[2][1]=0,tran.a[2][2]=1;
12     po[0]=0,po[1]=1; for (int i=2; i<=19; i++) po[i]=po[i-1]*10;
13     lim[0]=1; for (int i=1; i<=18; i++) lim[i]=lim[i-1]*10; lim[0]=2;
14     ans.a[0][0]=1,ans.a[1][0]=1,ans.a[2][0]=1;
15 }
16 Mat Mul(Mat u,Mat v) {
17     Mat w; int i,j,k;
18     for (i=0; i<3; i++)
19         for (j=0; j<3; j++)
20             for (k=0; k<3; k++)
21                 (w.a[i][j]+=u.a[i][k]*v.a[k][j])%=m;
22     return w;
23 }
24 Mat Qpow(Mat b,LL p) {
25     if (p==1) return b;
26     Mat t=Qpow(b,p/2); t=Mul(t,t);
27     return p%2==0?t:Mul(t,b);
28 }
29 int main() {
30     cin>>n>>m,pre();
31     for (int i=1; i<=18; i++) if (lim[i-1]<=n) {
32         tran.a[0][0]=po[i+1]%m;
33         if (i<18&&lim[i]-1<=n) ans=Mul(Qpow(tran,lim[i]-lim[i-1]),ans);
34         else if (lim[i]>n) ans=Mul(Qpow(tran,n-lim[i-1]+1),ans);
35     }else break;
36     cout<<ans.a[0][0]<<\n;
37     return 0;
38 }
View Code

 

以上是关于[luogu P3216] [HNOI2011]数学作业的主要内容,如果未能解决你的问题,请参考以下文章

[HNOI2008]越狱(luogu P3197)

luogu P1446 [HNOI2008]Cards

luogu P2234 [HNOI2002]营业额统计

luogu3244 bzoj4011 HNOI2015 落忆枫音

HNOI2011 数学作业

BZOJ2338[HNOI2011]数矩形 几何