UVA-OJ.关于数论的成长练习

1). UVA11582.Colossal Fibonacci Numbers! 

求斐波那契数列第 ( a ^ b ) 项模 N 的值。

斐波那契数列在模 N 的情况下会有循环节,也就是说当出现连续两项分别等于 F[ 0 ],F[ 1 ] 的时候我们就找到了循环节 T,所以循环节 T 一定在 N ^ 2 的长度内出现。因为 N 比较小,就可以先求出 F[ 1 ] ... F[ N ^ 2 ],然后答案就是 F[ ( a ^ b ) % T ]

#include <cstdio>
#include <cstring>

using namespace std;

typedef unsigned long long ULL;

ULL a, b;
int n, f[1000010];

inline ULL read()
{
	ULL ret = 0; char c = getchar();
	while (!(c >= '0' && c <= '9')) c = getchar();
	while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();
	return ret;
}

void init()
{
	a = read(); b = read();
	scanf("%d", &n);	
}

ULL mul(ULL a, ULL b, int n)
{
	ULL ret = 0;
	while (b){
		if (b & 1) ret = (ret + a) % n;
		a = (2 * a) % n;
		b >>= 1;
	}
	return ret;
}

int power(ULL a, ULL b, int n)
{
	ULL ret = 1;
	a %= n;
	while (b){
		if (b & 1) ret = mul(ret, a, n);
		a = mul(a, a, n);
		b >>= 1;
	}
	return (int)ret;
}

void doit()
{
	int n2 = n * n, r = 2;
	f[0] = 0 % n; f[1] = 1 % n;
	for (int i = 2; i <= n2; i ++){
		f[i] = (f[i - 1] + f[i - 2]) % n;
		if (f[i] == f[1] && f[i - 1] == f[0]){
			r = i - 1;
			break;
		}
	}
	int pos = power(a, b, r);
	printf("%d\n", f[pos] % n);
}

int main()
{
	int T; scanf("%d", &T);
	while (T --){
		init();
		doit();
	}
	return 0;
}

2).UVA12169.Disgruntled Judge

已知递推式 Xi = (a * Xi-1 + b) % 10001,并告诉你 X1,X3, X5 ... X2*t-1,请你求出 X2,X4,X6 ... X2*t

因为递推式模了 10001,所以 a 的取值范围是 [ 0, 10000 ],这样就可以枚举 a。已知了 a ,通过递推式可以推出 X1X3 之间的公式:X3 = ((a ^ 2) * X1 + a * b + b) % 10001,所以 ( a + 1 ) * b % 10001 = (X3 - ( a ^ 2 ) * X1) % 10001。这样就可以用扩展欧几里德求出 b,然后计算 Xi,看是否与已知的 X1,X3 ... X2*t-1 矛盾,如果矛盾则说明 a 不合法,否则就输出出来。

#include <cstdio>

using namespace std;

const int mod = 10001;
typedef long long LL;

int T;
LL s[10005];

void init()
{
	for (int i = 1; i <= T; i ++)
		scanf("%lld", &s[(i << 1) - 1]);
}

void ex_gcd(LL a, LL b, LL &d, LL &x, LL &y)
{
	if (!b){
		x = 1; y = 0; d = a;
		return;
	} else {
		ex_gcd(b, a % b, d, x, y);
		int t = x;
		x = y; y = t - a / b * x;
	}
}

void doit()
{
	s[2] = s[1];
	if(T >= 2){
		for (int a = 0; a <= 10000; a ++){
			LL A = a + 1, B = mod, C = s[3] - a * a * s[1];
			LL x, y, d;
			ex_gcd(A, B, d, x, y);
			if (C % d == 0){
				x = x * (C / d);
				int i;
				for (i = 1; i < T; i ++){	
					s[i << 1] = (s[(i << 1) - 1] * a + x) % mod;
					if (s[(i << 1) + 1] != ((s[i << 1] * a + x) % mod)) break;
				}
				//printf("%d %lld %lld\n", a, x, s[(T << 1) - 1]);
				s[T << 1] = (s[(T << 1) - 1] * a + x) % mod;
				if (i == T) break;
			}
		}
	}
	for (int i = 1; i <= T; i ++) printf("%lld\n", s[i << 1]);
}

int main()
{
	while (scanf("%d", &T) != EOF){
		init();
		doit();
	}
	return 0;
}

3).UVA10375.Choose and divide

求组合数 C(p, q) / C(r, s)

很有趣的题目,还是自己思考比较好,提示一下:唯一分解定理。

#include <cstdio>
#include <cstring>
#include <cmath>

using namespace std;

const int MAX_N = 10005;

int pri[1500], tot = 0;
bool check[MAX_N];
int p, q, r, s, cnt[1500];

inline void prime()
{
	memset(check, 0, sizeof(check));
	check[1] = 1;
	for (int i = 2; i <= 10000; i ++){
		if (!check[i]) pri[++ tot] = i;
		for (int j = 1; j <= tot; j ++){
			if (i * pri[j] > 10000) break;
			check[i * pri[j]] = 1;
			if (i % pri[j] == 0) break; 
		}
	}
}

void gao(int x, int d)
{
	for (int i = 1; i <= tot; i ++){
		while (x % pri[i] == 0){
			x /= pri[i];
			cnt[i] += d;
		}
		if (x == 1) break;
	}
}

void calc(int n, int d)
{
	for (int i = 1; i <= n; i ++)
		gao(i, d);
}
void doit()
{
	memset(cnt, 0, sizeof(cnt));
	calc(p, 1); calc(q, -1); calc(p - q, -1);
	calc(s, 1); calc(r - s, 1); calc(r, -1);
	double ans = 1.0;
	for (int i = 1; i <= tot; i ++)
		ans *= pow(pri[i], cnt[i]);
	printf("%.5f\n", ans);
}

int main()
{
	prime();
	while (scanf("%d%d%d%d", &p, &q, &r, &s) != EOF){
		doit();
	}
	return 0;
}

4).UVA10791.Minimum Sum LCM

输入一个整数 N,求至少两个整数,使得他们的最小公倍数为 N,且这些整数的和最小。输出最小的和。

利用唯一分解定理把 N 分解为 (a1 ^ p1) * (a2 ^ p2) * ... ,不难发现每个 (ai ^ pi) 作为一个单独的整数时最优。但是还有一些陷阱需要处理:N = 1 时答案为 1 + 1 = 2N 只有一种因子时需要加个 1,还要注意 N = 2 ^ 31 - 1 是不要溢出。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

using namespace std;

const int MAX_N = 1000005;
typedef long long LL;

int p[MAX_N], num[MAX_N], cnt = 0;
LL n;

void doit()
{
	cnt = 0;
	memset(p, 0, sizeof(p)); memset(num, 0, sizeof(num));
	if (n == 1) { printf("2\n"); return; }
	if (n == 2147483647) { printf("2147483648\n"); return; }
	LL t = n;
	for (int i = 2; i * i <= n; i ++){
		if (t % i == 0){
			p[++ cnt] = i;
			while (t % i == 0){
				t /= i;
				num[cnt] ++;
			}
		}
		if (t == 1) break;
	}
	if (t != 1) p[++ cnt] = t, num[cnt] ++;
	if (cnt == 1) {
		LL ans = pow(p[1], num[1]) + 1; printf("%lld\n", ans);
	}
	else {
		LL ans = 0;
		for (int i = 1; i <= cnt; i ++){
			ans += pow(p[i], num[i]);
		}
		printf("%lld\n", ans);
	}
}

int main()
{
	int tot = 0;
	while (cin >> n){
		if (n == 0) break;
		printf("Case %d: ", ++ tot);
		doit(); 
	}
	return 0;
}



 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值