• 洛谷P3216 [HNOI2011]数学作业


    洛谷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


    题解:想想看,如果n范围比较小时你会怎么做?
    我们应该想到,比如说对于序列1234567,要取模的数是13,那么我们会先让1对13取模得到1,再让12对13取模得到12,再让123对13取模得到6,再让64对13取模得到12,再让125对13取模得到8,再让86对13取模得到8,最后让87对13取模得到9。这与1234567直接对13取模的值是相同的。其中的数学证明不再赘述。
    于是我们可以得到递推式,对于序列f(n),f(n)=f(n-1)×10^len(n)+n(其中len(n)表示n这个数字有几位)。
    由于n的范围极大,我们考虑用矩阵快速幂转移优化,构造矩阵如下:
    f(n) 10^len(n) 1 1 f(n-1)
    n = 0 1 1 × n-1
    1 0 0 1 1
    我们转移时,根据1~9时len(i)相同,10~99时len(i)相同,100~999时len(i)相同划分为不同的区块进行转移优化。
    这个转移思路是从WHC大佬那里获得的,本蒟蒻还是太弱了。
    接下来还是看代码吧。
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 const int N=3;
     6 struct node{
     7     long long a[N][N];
     8     void clr(){memset(a,0,sizeof(a));}
     9 }ans,p[20];
    10 long long n,mod,now,t;
    11 node operator * (node x,node y)
    12 {
    13     node z; z.clr();
    14     for (int i=0;i<3;++i)
    15     for (int j=0;j<3;++j)
    16     for (int k=0;k<3;++k)
    17     z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j])%mod;
    18     return z;
    19 }
    20 node Pow(node x,long long y)
    21 {
    22     node tmp=x;
    23     for (;y;y>>=1,x=x*x) if (y&1) tmp=tmp*x;
    24     return tmp;
    25 }
    26 long long calc(long long x,long long y)
    27 {
    28     long long tmp=1;
    29     for (;y;y>>=1,x=x*x) if (y&1) tmp=tmp*x;
    30     return tmp;
    31 }
    32 int main()
    33 {
    34     scanf("%lld%lld",&n,&mod);
    35     ans.clr(); ans.a[2][0]=1;
    36     p[0].clr(); p[0].a[0][0]=p[0].a[0][1]=p[0].a[0][2]=p[0].a[1][1]=p[0].a[1][2]=p[0].a[2][2]=1;
    37     for (int i=1;i<=19;++i) p[i]=p[i-1],p[i].a[0][0]*=10,p[i].a[0][0]%=mod;
    38     for (long long i=0,j=9,k=0;k<min(n,(long long)999999999999999999);k+=9*calc(10,i),++i)
    39     ans=Pow(p[i+1],min(n-k,9*calc(10,i))-1)*ans;
    40     if (n==1000000000000000000) ans=p[19]*ans;
    41     printf("%lld
    ",ans.a[0][0]);
    42     return 0;
    43 }
    View Code
    
    
    
     
  • 相关阅读:
    Qt ‘/‘和‘\‘转化的方式
    Qt 快速转换路径(/斜杠与\反斜杠转换)
    Qt 网络编程:QNetworkAccessManager
    excel vba 单元格内部关键字突出显示改变颜色
    casbin 学习笔记【1】
    Linux
    AJAX
    Spring
    MyBatis
    Oracle查看表空间使用情况
  • 原文地址:https://www.cnblogs.com/zk1431043937/p/7801016.html
Copyright © 2020-2023  润新知