HDU_5666(GCD+大数)

题目链接:点击打开链接

根据题意,是让我们求该直线与第一象限构成的三角形区域中,坐标全为整数点的个数且不包括所连直线的整数点个数,坐标轴上的不算。那么我们可以用区域内的整数点减去所有边上的整数点,就是我们的答案。

1.首先我们求出三角形区域内的整数点,不包括斜边上的点的总个数是,1+2+......+q-2=(q-1)*(q-2)/2.

2.然后我们求出(0,0)与斜边上各整数点的连线上整数点的个数。而(0,0)到(x0,y0)所构成的直线上(除去(x0,y0)外),整数点的个数为gcd(x0,y0)-1.

下面证明一下这个结论...(0,0)到(x0,y0)这条直线方程为y=y0/x0 * x。上下同除以gcd(x0,y0)为,y = (y0/gcd)/(x0/gcd)*x..那么肯定(y0/gcd)与(x0/gcd)是互质的,则y想为整数,则x必须为x0/gcd的整数倍,x范围为0-x0,那么这个区间内的x0/gcd倍数个数为x0/(x0/gcd)=gcd,除去端点(x0,y0),所以为gcd(x0,y0)-1。。。。因此可以推广到一般的情况为(x0,y0)到(x1,y1)构成的直线上,整数点的个数为gcd(x1-x0,y1-y0).


所以我们求出gcd(x0,y0)-1即可,又因为x0+y0=q,且gcd(a,b)=gcd(a,a-b)(a>b)。。所以gcd(x0,y0)=gcd(x0,p-y0)=gcd(x0,p);  p为质数,,那么gcd(x0,p)只可能是p的倍数或者为1,,又因为x,y是小于p的,,,(x+y=p嘛),,所以gcd(x0,p)=gcd(x0,y0)=1.。。。则gcd(x0,y0)-1=0,那么其它直线上是没有整数点的。因此最终的结果就是(q-1)*(q-2)/2.。。


3。注意坑,,q的范围很大,,,(q-1)*(q-2)已经超过long long 了,所以要用到大数知识,,一是用java的大数类解决,二是用二进制模拟大数乘法得到。。我用的是二进制模拟....

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#include <stack>
#include <queue>
#include <map>
#include <vector>
#include <cmath>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
const int maxn = 55555;
int sum[maxn << 2];
typedef long long int LL;
LL ans(LL x, LL y, LL k)
{
	LL res = 0;
	int i, j;
	for (; y>0; y>>=1)                              //一直要将y的位数移完,结束条件为y=0
	{
		if (y & 1)
		{
			res += x;
			res %= k;                               //模无处不在
		}
		x = (x + x) % k;                            //扩大俩倍
	}
	return res;
}
int main(void)
{
	//freopen("in.txt", "r", stdin);
	LL q, P;
	int T;

	scanf("%d", &T);
	while (T--)
	{
		scanf("%lld%lld", &q, &P);
		//先算出总的个数
		//LL sum = (((q - 1)%P)*((q - 2)%P) / 2)%P;得用二进制模拟大数乘法,因为q范围太大了,(q-1)*(q-2)超出范围了
		//然后去掉在连线上的整数点即可
		//容易得出为0
		LL ans1 = (q - 1);
		LL ans2 = (q - 2);
		if (ans1 % 2 == 0)                             //一开始,就将这个2给去掉
			ans1 /= 2;
		else
			ans2 /= 2;
		LL sum = ans(ans1, ans2, P);                   //经过模之后除以2就不一定对了
		printf("%lld\n", sum);
	}
	return 0;
}

你可能感兴趣的:(数论,gcd,大数)