본문 바로가기
알고리즘/백준

백준 10830 행렬 제곱 C++

by ash9river 2024. 10. 6.

 

간단한 해설

 

A의 B제곱은 분할정복으로 다음과 같은 공식을 사용할 수 있다.

A^B=A^(B/2)*A^(B/2) (B is even)

A^B=A^((B-1)/2)*A^((B-1)/2)*A (B is odd)

 

여기서 B에 해당되는 값들을 잘 생각해보면 1, 2, 2^2, 2^3... 이런식으로 분할할 수 있다.

 

만약에 B가 5라고 생각해보자 그러면 B=2^2+1이라고 생각할 수 있다.

만약, B가 21이라고 생각하면 B=2^4+2^2+1이다.

 

그러면 A의 2제곱n지수들을 메모이제이션하고, B를 비트마스킹해서 그 값들을 곱하는 방식으로 사용하면 된다.

 

B의 최대치가 100000000000이니까 B=100000000000일때를 생각해보자.

 

B를 이진법으로 변환했을 때, 0000000000000000000000000001011101001000011101101110100000000000이 나온다.

그러므로, B&(1<<exponent)의 값이 B&(1<<exponent)일 경우(1이 나오는경우)에만 메모이제이션한 값을 사용하는거다.

 

다시 간단히 설명하자면 B가 21일 경우 memoi[4] x memoi[2] x memoi[0]이다.(memoi를 A의 2^n진법의 메모이제이션 배열이라 정의)

 

 

이제  B=100000000000일 때로 돌아가보자.

0000000000000000000000000001011101001000011101101110100000000000이니까

36 34 33 32 30 27 22 21 20 18 17 15 14 13 11일 때(제일 오른쪽이 0) 비트의 값이 1이다.

그러므로 A^100000000000은 A^(2^36+2^34+2^33+2^32+2^30+2^27+2^22+2^21+2^20+2^18+2^17+2^15+2^14+2^13+2^11)이다.

이는 (A^(2^36)) x (A^(2^34)) x (A^(2^33))...인 값이다.

 

그러므로 memoi[36] x memoi[34] x memoi[33]...이다.

 

이제 행렬 곱셈 계산만 잘 구현하면 끝이다. 

 

물론, 행렬 계산의 특성상, 초기값은 전부 0이나 1로 초기화된 값이 아닌 단위 행렬이어야한다.

 

 

 

참고로, 값을 구하는 과정에서 나머지연산은 단순해보이지만 사실 모듈러 연산의 성질을 알아야한다.

 

 

 

algorithm/페르마의_소정리 at main · ash9river/algorithm

Contribute to ash9river/algorithm development by creating an account on GitHub.

github.com

 

 

#include <iostream>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;
vector<vector<ll>> v;
vector<vector<vector<ll>>> memoi;
ll n,b;
vector<vector<ll>> makeCalc(vector<vector<ll>> one,vector<vector<ll>> two){
	vector<vector<ll>> sum(n,vector<ll>(n,0));
	for(int i=0;i<n;++i){
		for(int j=0;j<n;++j){
			for(int k=0;k<n;++k){
				sum[i][j]+=one[i][k]*two[k][j];
				sum[i][j]%=1000;
			}
		}
	}
	return sum;
}

vector<vector<ll>> calc(ll b){
	vector<vector<ll>> ret(n,vector<ll>(n,0));
	int ptr=0;
	for(int i=0;i<n;++i){
		ret[i][ptr]=1;
		++ptr;
	}
	for(ll i=63;i>=0;--i){
		if(b&((ll)1<<i)){
			ret=makeCalc(ret,memoi[i]);
		}
	}
	return ret;
} 

int main(){
	cin.tie(0);
	ios_base::sync_with_stdio(0);
	cin>>n>>b;
	v.resize(n,vector<ll>(n,0));
	for(int i=0;i<n;++i){
		for(int j=0;j<n;++j){
			cin>>v[i][j];
		}
	}
	int ptr=0;
	for(ll i=63;i>=0;--i){
		if(b&((ll)1<<i)){
			ptr=i+1;
			break;
		}
	}
	memoi.resize(ptr+1,vector<vector<ll>>(n,vector<ll>(n,98764321)));
 	for(int i=0;i<n;++i){
		for(int j=0;j<n;++j){
			memoi[0][i][j]=v[i][j];
		}
	}
	for(int k=1;k<ptr;++k){
		memoi[k]=makeCalc(memoi[k-1],memoi[k-1]);
	}
	vector<vector<ll>> ans=calc(b);
	for(int i=0;i<n;++i){
		for(int j=0;j<n;++j){
			cout<<ans[i][j]<<" ";
		}
		cout<<"\n";
	}
}

'알고리즘 > 백준' 카테고리의 다른 글

백준 16562 친구비 C++  (1) 2024.10.15
백준 27978 보물 찾기 2 C++  (0) 2024.10.10
백준 1956 운동 C++  (0) 2024.10.06
백준 17069 파이프 옮기기 2 C++  (2) 2024.10.02
백준 2294 동전 2 C++  (1) 2024.09.30