プロジェクトオイラー問520

プロジェクトオイラー問520


n桁の数字の中の
偶数個の偶数の数、奇数個の偶数の数、奇数個の奇数の数、偶数個の奇数の数
を主キーに漸化式を立て。
後は一桁と一桁で2桁。
2桁と2桁で4桁
4桁と4桁で8桁。
、、、

一桁を作り
1桁と2桁で3桁
3桁と2桁を1桁に足す。

1桁+2桁+3桁と4桁の直積で5,6,7桁
これに4桁を足して4,5、6,7、これに1,2,3桁を足せば7ケタ。
以下同様。
計算時間10分。

問題の

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<map>
const int MOD=1000000123;

struct E{
	int j,k,l,m;
	bool operator<(const E& e)const{
		if(j!=e.j)return j<e.j;
		if(k!=e.k)return k<e.k;
		if(l!=e.l)return l<e.l;
		return m<e.m;
	}
	void set(int j1,int k1,int l1,int m1){
		j=j1;
		k=k1;
		l=l1;
		m=m1;
	}
	void print(){
		std::cout<<"("<<j<<" "<<k<<" "<<l<<" "<<m<<")";
	}
};


int firstSet(E e1){
	int j=e1.j;
	int k=e1.k;
	int l=e1.l;
	int m=e1.m;
	if((j==0)&&(k==1)&&(l==0)&&(m==0))return 4;
	if((j==0)&&(k==0)&&(l==1)&&(m==0))return 5;
	return 0;
}
bool secondSet(E e1){
	int j=e1.j;
	int k=e1.k;
	int l=e1.l;
	int m=e1.m;
	if((k>0)||(m>0))return false;
	if((j>0)||(l>0))return true;
	return false;
}

int main(){
	//一桁の計算量が411*6
	//Q(100)

	__int64 as[7][7][7][7],as2[7][7][7][7];//[偶数個の偶数][奇数個の偶数の数][奇数個の奇数の数][偶数 個の奇数の数]
	__int64 ans=5;
	memset(as,0,sizeof(as));
	as[0][1][0][0]=4;
	as[0][0][1][0]=5;
	std::map<E,std::map<E,__int64> > count,sums,nori;
	std::map<E,std::map<E,__int64> > allCount;
	

	
	
	//まずは1桁と1桁で2桁を作り、その過程で漸化式の構造をcount変数で把握する
	//2段と2段で4段、4段と4段で
	//以後は1段と2段で3段を作る
	//4段と(3,2,1)段で(7,6,5)+4+(3,2,1)段を求め
	//この調子で大きな段を計算して行く
	
	E e1;
	e1.set(0,1,0,0);
	allCount[e1][e1]=1;
	e1.set(0,0,1,0);
	allCount[e1][e1]=1;
		
	for(int i=2;i<=2;i++){
		memset(as2,0,sizeof(as2));
		for(int j=0;j<6;j++){
			for(int k=0;k+j<6;k++){
				for(int l=0;l<6;l++){
					for(int m=0;m+l<6;m++){
						//偶数個の偶数を一つ増やす
						__int64 c=as[j][k][l][m];
						E e1,e2;
						e1.set(j,k,l,m);
						if(k<5){	
							if(j>0){
								as2[j-1][k+1][l][m]=(as2[j-1][k+1][l][ m]+c*j)%MOD;
								e2.set(j-1,k+1,l,m);
								count[e2][e1]+=j;
								nori[e2][e1]+=j;
								}
							if(j+k<5){
								as2[j][k+1][l][m]=(as2[j][k+1][l][m]+c*(5-j-k ))%MOD;
								e2.set(j,k+1,l,m);
								count[e2][e1]+=(5-j-k);
								nori[e2][e1]+=(5-j-k);

							}
						}
						//奇数個の偶数を一つ増やす
						if((0<k)&&(j<5)){
							as2[j+1][k-1][l][m]=(as2[j+1][k-1][l][m]+c*k)%MOD;
							e2.set(j+1,k-1,l,m);
							count[e2][e1]+=k;
							nori[e2][e1]+=k;
						}
						
						
						//奇数個の奇数を一つ増やす
						if((l>0)&&(m<5)){
							as2[j][k][l-1][m+1]=(as2[j][k][l-1][m+1]+c*l)%MOD;
							e2.set(j,k,l-1,m+1);
							count[e2][e1]+=l;
							nori[e2][e1]+=l;

						}
	
						//偶数個の奇数を一つ増やす。					
						if(l<5){
							if(m>0){
								as2[j][k][l+1][m-1]=(as2[j][k][l+1][m-1 ]+c*m)%MOD;
								e2.set(j,k,l+1,m-1);
								count[e2][e1]+=m;
								nori[e2][e1]+=m;
							}
							if(l+m<5){
								as2[j][k][l+1][m]=(as2[j][k][l+1][m]+c*(5-l-m ))%MOD;
								e2.set(j,k,l+1,m);
								count[e2][e1]+=(5-l-m);
 								nori[e2][e1]+=(5-l-m);
							}
						}
					}
				}
 			}
		}
		memcpy(as,as2,sizeof(as2));
		__int64 add=0;
		for(int i=0;i<6;i++){
			for(int j=0;j<6;j++){
				add=(add+as[i][0][j][0])%MOD;
			}
		}
	}
	ans=29;
	for(__int64 i=4;i<=pow(2,39);i*=2){

		
		//まずは2倍
		std::map<E,std::map<E,__int64> > count2,allCount2;
		std::map<E,std::map<E,__int64> >::iterator itA,itNori;
		std::map<E,__int64>::iterator itA2,itNori2,itB;
		
		E e1,e2,e3,e4,e5;
		__int64 p1,p2,p3,pAll,cc=0;
		
	//一回目は1段+2段=3段を計算
		for(itA=count.begin();itA!=count.end();itA++){
			e1=(*itA).first;
			for(itA2=(*itA).second.begin();itA2!=(*itA).second.end();itA2++){
				e2=(*itA2).first;
 				p1=(*itA2).second;
				for(itNori2=nori[e2].begin();itNori2!=nori[e2].end();itNori2++){
					e3=(*itNori2).first;
				p2=(*itNori2).second;
 					for(itB=allCount[e3].begin();itB!=allCount[e3].end();itB++){
					e4=(*itB).first;
						p3=(*itB).second;
						pAll=(p1*p2)%MOD;
						pAll=(pAll*p3)%MOD;
						allCount2[e1][e4]+=pAll;
						allCount2[e1][e4]%=MOD;
						cc++;
					}
				}
			}
		}
		//最初は3段を追加
		for(itA=allCount2.begin();itA!=allCount2.end();itA++){
			E e1=(*itA).first;
			for(itA2=(*itA).second.begin();itA2!=(*itA).second.end();itA2++){
				E e2=(*itA2).first;
				allCount[e1][e2]+=(*itA2).second;
				allCount[e1][e2]%=MOD;
			}
		}
		//2^i段を追加
		for(itA=count.begin();itA!=count.end();itA++){
			e1=(*itA).first;
 			for(itA2=(*itA).second.begin();itA2!=(*itA).second.end();itA2++){
				e2=(*itA2).first;
				allCount[e1][e2]+=(*itA2).second;
				allCount[e1][e2]%=MOD;
			}	
		}
		
		__int64 add2=0;
		for(itA=allCount.begin();itA!=allCount.end();itA++){
			E e1=(*itA).first;
			for(itA2=(*itA).second.begin();itA2!=(*itA).second.end();itA2++){
				E e2=(*itA2).first;
				cc++;
				if((firstSet(e2)>0)&&(secondSet(e1))){
					add2+=firstSet(e2)*allCount[e1][e2];
					add2%=MOD;
 				}
			}
		}
		
		
		
		std::cout<<i<<" "<<add2<<" ";
 		//2^n段だけを計算
		for(itA=count.begin();itA!=count.end();itA++){
			e1=(*itA).first;
			for(itA2=(*itA).second.begin();itA2!=(*itA).second.end();itA2++){
				e2=(*itA2).first;
				p1=(*itA2).second;
				for(itNori2=nori[e2].begin();itNori2!=nori[e2].end();itNori2++){
					e3=(*itNori2).first;
					p2=(*itNori2).second;
					for(itB=count[e3].begin();itB!=count[e3].end();itB++){
 						e4=(*itB).first;
						p3=(*itB).second;
						pAll=(p1*p2)%MOD;
						pAll=(pAll*p3)%MOD;
						count2[e1][e4]+=pAll;
						count2[e1][e4]%=MOD;
						cc++;
					}
 				}
			}
		}
		
		count.clear();
 		count.insert(count2.begin(),count2.end());
		__int64 add=0;
		for(itA=count2.begin();itA!=count2.end();itA++){
			E e1=(*itA).first;
			for(itA2=(*itA).second.begin();itA2!=(*itA).second.end();itA2++){
 				E e2=(*itA2).first;
 				if((firstSet(e2)>0)&&(secondSet(e1))){
					add+=firstSet(e2)*count[e1][e2];
					add%=MOD;
				}
			}
		}
 		std::cout<<i<<" cc="<<cc<<" add="<<add<<" all="<<add+add2<<"\n";
		ans+=add+add2;
		ans%=MOD;
	}
	
	std::cout<<"Σi=1~39 Q(2^i)="<<ans;
}
最終更新:2015年07月21日 08:21