1. 程式人生 > >洛谷P3216 [HNOI2011]數學作業

洛谷P3216 [HNOI2011]數學作業

nat sans ostream play 希望 span spa cat 思路

洛谷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\n",ans.a[0][0]); 42 return 0; 43 }
View Code

 

洛谷P3216 [HNOI2011]數學作業