vijos 2035 奇數偶數與絢麗多彩的數
描述
Q先生是一個熱愛學習的男孩子。
他認為一個 n 位的正整數 x 若能被稱作是絢麗多彩的,一定要滿足對於{1,3,5,7,9} 中任意一個奇數或者沒有在 x 中出現,或者在 x 中出現了恰好奇數次;同時對於 {0,2,4,6,8} 中任意的偶數或者沒有在 x 中出現,或者在x 中出現了偶數次。同時需要註意 x 是不能有前導零的。
例如 141221242 就是一個九位的絢麗多彩的數。
現在Q先生給定了正整數 n 與另外一個正整數 p,希望你統計出來一共有多少不超過 n 位的絢麗多彩的數,並輸出模 p後的余數。
格式
輸入格式
輸入有一行,包含兩個由空格隔開的正整數,分別為 n 和 p。
輸出格式
輸出一個正整數,表示不超過 n 位的絢麗多彩數的總數模 p 後的余數。
樣例1
樣例輸入1
7 1000000123
樣例輸出1
287975
樣例2
樣例輸入2
100 1000000123
樣例輸出2
123864868
限制
對於100%的數據,2<=n<=2^60,10^9<=p<=2*10^9。
昨晚打的某個比賽的題。
我們發現偶數出現偶數次和不出現都等價於出現的次數%2=0,奇數出現奇數次相當於出現次數%2=1,當然奇數還可能不出現。所以我們可以枚舉哪些奇數是不出現的,然後剩下的奇數滿足出現奇數次,偶數出現偶數次就行了。
還可以發現我們沒有必要知道每個奇數/偶數出現的次數的奇偶性,只需要知道有多少個 奇數/偶數 出現 奇數/偶數 次就行了。也就是狀態壓縮之後,我們只需要一個兩位的五進制數就可以表示一個狀態。
而我們在外層也可以直接枚舉有幾個奇數出現過,然後再將這種情況下的方案總數乘上一個組合數就行了。
但是這個題要求的數是<=n位的沒有前導零的滿足條件的數的個數,所以我們要求的最終轉移方陣不是某個方陣A的n-1次方,而是A^0+A^1+....A^(n-1)。這個推一推式子就可以用類似矩陣快速冪的方法求出。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<algorithm> #include<cstring> #define ll long long using namespace std; ll N,P,n,ans=0,C[10][10]; inline ll add(ll x,ll y){ x+=y; return x>=P?x-P:x; } struct node{ ll a[41][41]; inline void clear(){ memset(a,0,sizeof(a)); } inline void init(){ clear(); for(int i=0;i<n;i++) a[i][i]=1; } node operator *(const node &u)const{ node r; r.clear(); for(int k=0;k<n;k++) for(int i=0;i<n;i++) for(int j=0;j<n;j++) r.a[i][j]=add(r.a[i][j],a[i][k]*(ll)u.a[k][j]%P); return r; } node operator +(const node &u)const{ node r; for(int i=0;i<n;i++) for(int j=0;j<n;j++) r.a[i][j]=add(a[i][j],u.a[i][j]); return r; } }base,ANS; inline node ksm(node x,ll y){ node r,q,BA=x; r.init(),q.init(); int i=60; for(;i;i--) if((1ll<<i)&y) break; r=x,i--; for(;i>=0;i--) if((1ll<<i)&y){ node O=x*BA; r=r*(q+O)+O; x=x*O; } else{ r=r*(q+x); x=x*x; } return r+q; } inline void solve(){ for(int i=0;i<=5;i++){ base.clear(); n=(i+1)*6; for(int j=0,X,Y;j<n;j++){ X=j%6,Y=j/6; if(X) base.a[j][j-1]=X; if(X<5) base.a[j][j+1]=5-X; if(Y) base.a[j][j-6]=Y; if(Y<5) base.a[j][j+6]=i-Y; } ANS=ksm(base,N-1); ans=add(ans,C[5][i]*(ll)((4*ANS.a[1][i*6]+i*ANS.a[6][i*6])%P)%P); } printf("%lld\n",ans); } int main(){ C[0][0]=1; for(int i=1;i<=5;i++){ C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; } scanf("%lld%lld",&N,&P); solve(); return 0; }
vijos 2035 奇數偶數與絢麗多彩的數