[JLOI2015] 有意义的字符串 题解

拿到题目,我们首先分析一下这个奇怪的式子:

(b+d2)n mod p

重点肯定是在里面的那个式子里面,最显眼的肯定也就是那个 d,根据整体形式,我们可以联系一元二次方程的求根公式 x=b±b24ac2a,这里也是一个根号,并且和我们要求的极为相似。于是乎我们尝试着将原式转换成一个方程的根。设原方程为 Ax2+Bx+C=0,根据对照可以列出方程:

{2A=2B=bB24AC=d

解得:

{A=1B=bC=b2d4

因此原方程转变成了:x2bx+b2d4=0。由于每一项的次数呈递减,所以我们考虑移项,看看是否会形成一个转移式子:

x2=bxb2d4

这个非常明显,是个标准的转移式子。这个时候就可以想到朴素 DP 大法,按照次数设计状态,然后根据前面的次数进行转移。

但原方程会有两个根,就是 b±d2。所以直接的转移会包括 bd2 带来的影响值。那么我们需要将 DP 进行合理的定义。设 dpn 表示 (b+d2)n+(bd2)n 的值,这个时候我们只需要拿 dpn 减去 (bd2)n 的影响值就可以了。

轻松写出转移式子:

dpi=b×dpi1b2d4×dpi2

但是由于这个次数超出了 int 范围,所以考虑进行优化,这种对极大 n 的优化,我们不难想到矩阵快速幂优化。我们把转移方程等号右边涉及到的变量提取出来:dpi1dpi2,因此初始矩阵是一个 1×2 的矩阵 ,而对应的常数项就是 bb2d4,所以转移矩阵是一个 2×2 的矩阵。初始矩阵 st 和转移矩阵 a 如下:

st=(dpi1dpi2)

a=(b1b2d40)

st 的初始化就是 n=1n=0 的情况。

然后我们突然发现一个问题,我们减去的 (bd2)n 是一个带根号的式子,我们该如何求出这个向下取整的值呢?

就在一筹莫展之际,那个亮眼的数据范围映入眼帘:b2d<(b+1)2。我们尝试将 d 带入进去,bd<b+1。所以 1<bd40

由于 |bd4|<1n 是个非负整数,所以 0|(bd2)n|<1,此时进行分类讨论:

  • 如果 bd=0,则 (bd2)n=0,向下取整为 0

  • 如果 bd<0,且 2n,此时原式会变成正数,则 0<(bd2)n<1,向下取整为 0

  • 如果 bd<0,且 2n,此时原式依然是负数,则 1<(bd2)n<0,向下取整为 1

综上所述,(bd2)n 的影响值无外乎就是 0 或者 1,计算的时候,我们判断第三种情况就可以了。

最后由于模数 p=75284434125795769377×1018,计算过程很有可能爆 long long,所以我们可以开一个老祖宗 __int128,写个快读快写,就可以了过了。

代码如下:

#include<bits/stdc++.h>
#define int __int128//懒 
using namespace std;
const int MAXN=83;
const int MOD=7528443412579576937;
int b,d,n;
void read(int &x)
{
	x=0;
	short flag=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')	flag=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	x*=flag;
}
int quick_mul(int x,int y)//龟速乘,可能没有必要但是就是想写 
{
	int res=0;
	while(y)
	{
		if(y&1)	res=res+x,res%=MOD;
		x=x+x,x%=MOD;
		y>>=1;
	}
	return res;
}
void mul(int f[3],int a[3][3])//转移模板 
{
    int c[3];
    memset(c,0,sizeof(c));
    for(int j=1;j<=2;j++)
    {
        for(int k=1;k<=2;k++)    c[j]=(c[j]+quick_mul(f[k],a[k][j]))%MOD;
    }
    memcpy(f,c,sizeof(c));
}
void mulself(int a[3][3])//矩阵自乘模板 
{
    int c[3][3];
    memset(c,0,sizeof(c));
    for(int i=1;i<=2;i++)
    {
        for(int j=1;j<=2;j++)
        {
            for(int k=1;k<=2;k++)    c[i][j]=(c[i][j]+quick_mul(a[i][k],a[k][j]))%MOD;
        }
    }
    memcpy(a,c,sizeof(c));
}
void write(int x)
{
	if(x<10)
	{
		putchar(x+'0');
		return;
	}
	write(x/10);
	putchar(x%10+'0');
}
signed main()
{
	read(b),read(d),read(n);
	if(!n)//特判 
	{
		puts("1");
		return 0;
	}
	if(n==1)//特判 
	{
		long long B=b,D=d;//玄学调试 
		cout<<((B+sqrt(D)))/2;
		return 0;
	}
	int f[3];
	memset(f,0,sizeof(f));
	int a[3][3];
	memset(a,0,sizeof(a));
	a[1][1]=b,a[1][2]=1,a[2][1]=-(b*b-d)/4;
	f[2]=b,f[1]=(b*b+d)/2;//构造两个矩阵 
	n-=2;
	int temp=n;//n和n-2的奇偶性相同,所以提前储存n-2不会错 
	while(n)//快速幂模板 
	{
		if(n&1)	mul(f,a);
		mulself(a);
		n>>=1;
	}
	if(b*b!=d&&!(temp&1))	f[1]--;//特判第三种情况 
	write(f[1]);//输出 
	return 0;//华丽收场 
}
posted @   Supor__Shoop  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· 2分钟学会 DeepSeek API,竟然比官方更好用!
· .NET 使用 DeepSeek R1 开发智能 AI 客户端
· DeepSeek本地性能调优
· 一文掌握DeepSeek本地部署+Page Assist浏览器插件+C#接口调用+局域网访问!全攻略
Document
点击右上角即可分享
微信分享提示