JZOJ 6481. 【GDOI2020模拟02.22】黎曼几何(矩阵乘法)
JZOJ 6481. 【GDOI2020模拟02.22】黎曼几何
题解
- 设 f n , 1 f_{n,1} fn,1和 f n , 2 f_{n,2} fn,2分别表示将 n n n个硬币移动 1 1 1格和 2 2 2格的最小步数,
- 推一推可以得到转移:
- f n , 1 = f n − 1 , 2 × 2 + 1 f_{n,1}=f_{n-1,2}\times 2+1 fn,1=fn−1,2×2+1
- f n , 2 − f n − 1 , 1 + f n − 1 , 2 × 2 + 2 f_{n,2}-f_{n-1,1}+f_{n-1,2}\times 2+2 fn,2−fn−1,1+fn−1,2×2+2
- 具体解释:
- 移动 1 1 1格,先将 n − 1 n-1 n−1个移动 2 2 2格,再把最下面的移动 1 1 1格,最后把 n − 1 n-1 n−1个移动 2 2 2格到达原来的第二格;
- 移动 2 2 2格,先将 n − 1 n-1 n−1个移动 2 2 2格,再把最下面的移动 1 1 1格,接着 n − 1 n-1 n−1个移动 1 1 1格,最下面的移动 1 1 1格,最后把 n − 1 n-1 n−1个移动两格达到原来的第三格。
- 但是这样是 O ( n ) O(n) O(n)的,优化很显然是用矩阵乘法,
- 具体实现需要减小常数。
代码
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define md 998244353
ll n;
ll f[3][100010][3][3],p[3][3],q[3][3];
ll read()
{
ll s=0;
char x=getchar();
while(x<'0'||x>'9') x=getchar();
while(x>='0'&&x<='9') s=s*10+x-48,x=getchar();
return s;
}
int main()
{
int tn,i,j,k,l,h;
scanf("%d",&tn);
memset(f,0,sizeof(f));
f[0][1][0][0]=1;
f[0][1][0][1]=1;
f[0][1][0][2]=2;
f[0][1][1][2]=1;
f[0][1][2][1]=2;
f[0][1][2][2]=2;
for(i=0;i<3;i++)
{
if(i)
{
for(j=0;j<3;j++)
for(k=0;k<3;k++)
for(l=0;l<3;l++) f[i][1][j][k]=(f[i][1][j][k]+f[i-1][99999][j][l]*f[i-1][1][l][k]%md)%md;
}
for(j=2;j<=99999;j++)
{
for(k=0;k<3;k++)
for(l=0;l<3;l++)
for(h=0;h<3;h++) f[i][j][k][l]=(f[i][j][k][l]+f[i][j-1][k][h]*f[i][1][h][l]%md)%md;
}
}
ll ans1=0,ans2=0;
while(tn--)
{
n=read();
n--;
for(i=0;i<3;i++)
for(j=0;j<3;j++) p[i][j]=i==j;
int i=0;
while(n)
{
int x=n%100000;
n/=100000;
if(x)
{
for(j=0;j<3;j++)
for(k=0;k<3;k++)
{
q[j][k]=0;
for(l=0;l<3;l++) q[j][k]=(q[j][k]+p[j][l]*f[i][x][l][k]%md)%md;
}
for(j=0;j<3;j++)
for(k=0;k<3;k++) p[j][k]=q[j][k];
}
i++;
}
ll s1=(p[0][1]+p[1][1]+p[2][1]*2)%md;
ll s2=(p[0][2]+p[1][2]+p[2][2]*2)%md;
ans1^=s1,ans2^=s2;
}
printf("%lld %lld",ans1,ans2);
return 0;
}
哈哈哈哈哈哈哈哈哈哈