[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 3View Codeusing 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 }
[luogu P3216] [HNOI2011]數學作業