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} imes 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} imes 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;
}