【HDU3292】数学_佩尔方程

1.题目链接。说的是滥竽充数的故事,但是抽象出来之后就是这样一个问题:原来有x*x个人,走了一个,把这x*x-1划分成N个y*y,求满足的(x,y),但是因为数量很多,所以求第k大的x,并且输出x%8191.

2.分析:根据题意我们就知道这个方程是怎么列的:x^2-Ny^2=1。就是求第k大的x。这是典型的佩尔方程,是数论种最经典的内容之一。它和线性代数里面的很多东西有着千丝万缕的关系。对于这个方程,只有在N不是完全平方数的时候有解,如果是完全平方数就无解。证明方法很简单,如果N是完全平方数,那么等式的左边可以进行因式分解,由于都是整数,所以二者乘起来不会是1。并且,一旦这个方程有解,那么就有无数组解。这也是很好证明的,就不再证了。具体的递推关系如下:

 

                    

                             

知道了这些,我们只需要把(x1,y1)算出来,我们又知道,我们研究的都是正整数解,所以根据递推式我们知道这个解是递增的,所以我们要求的第k大的元素,就是上边的那个式子,转移矩阵的k-1次幂乘初始的矩阵,这个使用矩阵快速幂就轻松搞定。所以我们首先暴力的把(x1,y1)算出来(这里有些人可能会觉得,(1,0)不是显然的是一组解吗,但是注意,我们这里研究的是正整数解,所以0是不符合的,对于N一直在变的方程,这个正整数解是不固定的,需要我们自己求解)。


void getans(ll &x, ll &y, ll d) 
{
	y = 1;
	while (1 > 0) 
	{
		x = (ll)sqrt(1 + d * y*y);
		if (x*x - d * y*y == 1) break;
		y++;
	}
}

 

有了x1,y1.那么问题解决,AC代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#pragma warning(disable:4996)
//检查a是不是完全平方数
int check(int a)
{
	double ans = sqrt(a);
	if (ans - (int)ans == 0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
//函数参数输入,返回值输出
//特别注意lenth一定要改,不然每次都遍历到最大的矩阵会tle
ll lenth = 2;
ll mod= 8191;
struct Sarray 
{
	ll data[2][2];
	//看着改
	Sarray operator *(const Sarray&a) 
	{
		Sarray tem;
		for (int i = 0; i<lenth; i++) 
		{
			for (int j = 0; j<lenth; j++) 
			{
				for (int k = 0; k<lenth; k++) 
				{
					tem.data[i][j] = (tem.data[i][j] + data[i][k] * a.data[k][j]) % mod;
				}
			}
		}
		return tem;
	}

	Sarray operator +(const Sarray&a) {
		Sarray tem;
		for (int i = 0; i<lenth; i++) {
			for (int j = 0; j<lenth; j++) {
				tem.data[i][j] = (data[i][j] + a.data[i][j]) % mod;
			}
		}
		return tem;
	}

	Sarray(const Sarray&a) {
		for (int i = 0; i<lenth; i++) {
			for (int j = 0; j<lenth; j++) {
				data[i][j] = a.data[i][j];
			}
		}
	}

	Sarray() 
	{
		memset(data, 0, sizeof(data));
	}

};

Sarray E;
void ini() 
{
	for (int i = 0; i<lenth; i++)
		for (int j = 0; j<lenth; j++)
			if (i == j)E.data[i][j] = 1;
			else E.data[i][j] = 0;
}

Sarray qpow(Sarray a, int b) {
	Sarray tem = E;
	while (b) {
		if (b & 1)tem = a * tem;
		a = a * a;
		b >>= 1;
	}
	return tem;
}
void getans(ll &x, ll &y, ll d) 
{
	y = 1;
	while (1 > 0) 
	{
		x = (ll)sqrt(1 + d * y*y);
		if (x*x - d * y*y == 1) break;
		y++;
	}
}
int main()
{
	ll N, K;
	while (~scanf("%lld%lld", &N, &K))
	{
		if (check(N))
		{
			printf("No answers can meet such conditions\n");
		}
		else
		{
			Sarray a;
			ll x1, y1;
			getans(x1, y1, N);
			//初始化矩阵
			a.data[0][0] = x1;
			a.data[0][1] = N*y1;
			a.data[1][0] = y1;
			a.data[1][1] = x1;
			ini();
			Sarray b = qpow(a, K - 1);
			int xk = b.data[0][0] * x1 + b.data[0][1] * y1;
			xk %= mod;
			printf("%d\n", xk);
		}
	}

	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值