• 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} imes 2+1 fn,1=fn1,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,2fn1,1+fn1,2×2+2
    • 具体解释:
    • 移动 1 1 1格,先将 n − 1 n-1 n1个移动 2 2 2格,再把最下面的移动 1 1 1格,最后把 n − 1 n-1 n1个移动 2 2 2格到达原来的第二格;
    • 移动 2 2 2格,先将 n − 1 n-1 n1个移动 2 2 2格,再把最下面的移动 1 1 1格,接着 n − 1 n-1 n1个移动 1 1 1格,最下面的移动 1 1 1格,最后把 n − 1 n-1 n1个移动两格达到原来的第三格。
    • 但是这样是 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;
    }
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    [LeetCode]Linked List Cycle
    ACM 整数划分(四)
    ACM 子串和
    ACM 阶乘之和
    ACM 组合数
    ACM 阶乘的0
    ACM 比大小
    ACM 擅长排列的小明
    ACM 重建二叉树
    cocos2dx 魔塔项目总结(一)
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910042.html
Copyright © 2020-2023  润新知