1. 程式人生 > >vijos 2035 奇數偶數與絢麗多彩的數

vijos 2035 奇數偶數與絢麗多彩的數

node def clear 個推 jos 輸出 line bre n-1

描述

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 奇數偶數與絢麗多彩的數