URAL 1132 Square Root

题目链接

题目:给一个素数n(≤32767),一个整数满足gcd(a,n) = 1,求x使得x * x ≡ a (mod n). 数据可能高达100000组。

分析:网上有很详细的用二次剩余相关知识求解的方法,这里给一个不用那么高深的知识的方法(主要是因为自己数论知识不够...)。

首先经过分析:若x,y同为方程的解,则

x² ≡ y² (mod n) 

故 (x + y)(x - y) ≡ 0 (mod n)

得到 x + y = n

也就是说:若x为同余方程的解,则n-x也是一个解(注意n=2的情况例外),并且也只可能有这两个解。

就用这些数论知识就够了,接下来看如何解题。

最朴素的方法,对于每一个方程,枚举从1到n/2的所有数,这当然会TLE。再结合题目给的数据范围,n不超过32767,在这个范围内总共大概有3000多个素数,是不是可以考虑打表?提前把所有素数以及对应的同余方程的解求出来,枚举次数为素数的和的一半,简单估算一下,相当于2.5 * 10⁷,这是可以接受的数据量!

时间问题就算解决了,问题在于打表的数据量太大,空间不够用了。解决办法为,对于n相同的数据,一次性处理完,这样每次只用记录一个素数对应的全部解,因此要先把所有待求数据读取并记录,然后把n相同的数据放到一起(排序),求解完毕后再把数据顺序还原(读取数据时记录id,按id排序即可还原顺序)。额外牺牲的时间为两次排序操作,这是可以接受的。最终通过时间为400ms。

//保存读取数据 
struct Case
{
	int a;
	int p;
	int id;
	int x;
} cs[100000];

int x[32768];

bool cmp1(const Case &a, const Case &b)
{
	return a.p < b.p;
}

bool cmp2(const Case &a, const Case &b)
{
	return a.id < b.id;
}

int main(int argc, char *argv[])
{
	int t, a, n;
	cin >> t;
	for (int i = 0; i < t; i++)
	{
		scanf("%d%d", &cs[i].a, &cs[i].p);
		cs[i].a %= cs[i].p;
		cs[i].id = i;
	}
	//按每组数据的素数大小排序 
	sort(cs, cs + t, cmp1);
	for (int i = 0; i < t;)
	{
		int p = cs[i].p;
		memset(x, 0, sizeof(int) * p);
		for (int j = 1; j <= (p >> 1); j++)
		{
			x[j * j % p] = j;
		}
		//求解p相同的数据 
		while (i < t && cs[i].p == p)
		{
			cs[i].x = x[cs[i].a];
			i++;
		}
	}
	//还原数据顺序 
	sort(cs, cs + t, cmp2);
	for (int i = 0; i < t; i++)
	{
		if (cs[i].x) 
		{
			if (cs[i].p != 2) printf("%d %d\n", cs[i].x, cs[i].p - cs[i].x);
			else puts("1"); 
		}
		else puts("No root");
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值