【校内模拟】《名字很高端但是和OI半毛钱关系没有的题#1》(矩阵快速幂)(BSGS)

简要题意:

汉诺塔,三个盘子成环,只允许将盘子挪到顺时针下一个,问把 a a a 柱上的所有盘子全部挪到 b b b 柱上至少需要几次操作,挪到 c c c 又要几次操作。


a n a_n an 表示把 n n n 个盘子全部挪到顺时针下一个位置的操作次数 , b n b_n bn 同理,不过是挪到下下个位置。

手玩可以发现递推式:

a n = 2 b n − 1 + 1 b n = 2 b n − 1 + 2 + a n − 1 a_n=2b_{n-1}+1\\b_n=2b_{n-1}+2+a_{n-1} an=2bn1+1bn=2bn1+2+an1

由于变化是线性的,矩阵快速幂转移即可。

由于询问次数有点爆炸,BSGS预处理即可。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

namespace IO{
	inline char gc(){
		static cs int Rlen=1<<22|1;static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}template<typename T>T get_integer(){
		char c;bool f=false;while(!isdigit(c=gc()))f=c=='-';T x=c^48;
		while(isdigit(c=gc()))x=((x+(x<<2))<<1)+(c^48);return f?-x:x;
	}inline int gi(){return get_integer<int>();}
	inline ll gl(){return get_integer<ll>();}
}using namespace IO;

using std::cerr;
using std::cout;

cs int mod=998244353;

struct mat{
	int a[3][3];mat(){memset(a,0,sizeof a);}
	int* operator[](int o){return a[o];}
	cs int* operator[](int o)cs{return a[o];}
};

mat operator*(cs mat &A,cs mat &B){
	mat C;
	C[0][0]=((ll)A[0][0]*B[0][0]+(ll)A[0][1]*B[1][0]+(ll)A[0][2]*B[2][0])%mod;
	C[0][1]=((ll)A[0][0]*B[0][1]+(ll)A[0][1]*B[1][1]+(ll)A[0][2]*B[2][1])%mod;
	C[0][2]=((ll)A[0][0]*B[0][2]+(ll)A[0][1]*B[1][2]+(ll)A[0][2]*B[2][2])%mod;
	C[1][0]=((ll)A[1][0]*B[0][0]+(ll)A[1][1]*B[1][0]+(ll)A[1][2]*B[2][0])%mod;
	C[1][1]=((ll)A[1][0]*B[0][1]+(ll)A[1][1]*B[1][1]+(ll)A[1][2]*B[2][1])%mod;
	C[1][2]=((ll)A[1][0]*B[0][2]+(ll)A[1][1]*B[1][2]+(ll)A[1][2]*B[2][2])%mod;
	C[2][0]=((ll)A[2][0]*B[0][0]+(ll)A[2][1]*B[1][0]+(ll)A[2][2]*B[2][0])%mod;
	C[2][1]=((ll)A[2][0]*B[0][1]+(ll)A[2][1]*B[1][1]+(ll)A[2][2]*B[2][1])%mod;
	C[2][2]=((ll)A[2][0]*B[0][2]+(ll)A[2][1]*B[1][2]+(ll)A[2][2]*B[2][2])%mod;
	return C;
}

mat A;

cs int K=1<<10|7;

mat pw0[K],pw1[K],pw2[K],pw3[K];

void Main(){
	A[0][0]=1,A[0][1]=2,A[0][2]=1;
	pw0[1][0][0]=0,pw0[1][0][1]=1,pw0[1][0][2]=0;
	pw0[1][1][0]=2,pw0[1][1][1]=2,pw0[1][1][2]=0;
	pw0[1][2][0]=1,pw0[1][2][1]=2,pw0[1][2][2]=1;
	pw0[0][0][0]=pw0[0][1][1]=pw0[0][2][2]=1;
	pw1[0]=pw2[0]=pw3[0]=pw0[0];
	for(int re i=2;i<=(1<<10);++i)pw0[i]=pw0[i-1]*pw0[1];
	pw1[1]=pw0[1<<10];
	for(int re i=2;i<=(1<<10);++i)pw1[i]=pw1[i-1]*pw1[1];
	pw2[1]=pw1[1<<10];
	for(int re i=2;i<=(1<<10);++i)pw2[i]=pw2[i-1]*pw2[1];
	pw3[1]=pw2[1<<10];
	for(int re i=2;i<=(1<<10);++i)pw3[i]=pw3[i-1]*pw3[1];
	int T=gi(),a=0,b=0;
	while(T--){ll n=gl()-1;
		mat res=A*(pw0[n&1023]*pw1[n>>10&1023])*(pw2[n>>20&1023]*pw3[n>>30&1023]);
		a^=res[0][0],b^=res[0][1];
	}cout<<a<<" "<<b<<"\n";
}

inline void file(){
#ifdef zxyoi
	freopen("riemannian.in","r",stdin);
#else
#ifndef ONLINE_JUDGE
	freopen("riemannian.in","r",stdin);
	freopen("riemannian.out","w",stdout);
#endif
#endif
}
signed main(){file();Main();return 0;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值