简单的欧几里德与扩展欧几里德

原创 2015年11月19日 10:55:49

1).扩展欧几里德的起手题——NOIP2012.同余方程

其实就是模版。

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

2).欧几里德的应用——bzoj1385.Division expression

除法表达式有如下的形式: X1/X2/X3…/Xk,其中 Xi 是正整数且 Xi<=1000000000 (1 <= i <= k, k <=10000)。除法表达式应当按照从左到右的顺序求,例如表达式1/2/1/2 的值为 1/4. 但可以在表达式中加入括号来改变计算顺序,例如 (1/2)/(1/2) 的值为 1. 现给出一个除法表达式 E,求是告诉是否可以通过增加括号来使其为E’, E’ 为整数。

首先E’一定可以表示成 X / Y 的形式。那么我们考虑分子分母上分别有那些数:第一,X1 一定出现在分子上。第二,不论怎么加括号 X2一定出现在分母上。第三其余的都可以出现在分子上。总之一句话,只有 X2 一定出现在分母上 —— X1/[X2/(X3/.../Xk)] ①。也就是说如果 ① 式是个整数,那么 E’就可以是整数,否则不论怎么加括号都不可能使E’为整数,因为 X2 总在分母上且总不能被消去。那么我们用分别求出 gcd(X2, Xi), 然后X2/gcd(X2, Xi)。如果最后 X21,那么可以为整数,否则就说明X2 总有不能被消去的因子。

#include <cstdio>
 
using namespace std;
 
const int MAX_N = 10005;
 
int T, n, a[MAX_N];
 
void init()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++) 
        scanf("%d", &a[i]);
}
 
int gcd(int a, int b)
{
    return (b == 0) ? a : gcd(b, a % b);
}
 
bool judge()
{
    a[2] /= gcd(a[1], a[2]);
    for (int i = 3; i <= n; i ++) a[2] /= gcd(a[i], a[2]);
    if (a[2] == 1) return 1;
    return 0;
}
 
void doit()
{
    if (judge()) printf("YES\n");
    else printf("NO\n");
}
 
int main()
{
    scanf("%d", &T);
    while (T --){
        init();
        doit();
    }
    return 0;
}

3).更多的扩展欧几里德

1.bzoj1477 && poj1061.青蛙的约会

有两只青蛙分别在一个长度为 L的环的 X 处和 Y 处,第一只每次可以跳 M 个单位距离,而第二只可以跳 N 个,问最少跳几次后能相遇,如果不能输出 "Impossible"

设跳了 t 次,X 青蛙跳的距离可以表示为:X + MtY 青蛙跳的距离可以是:Y + Nt,而如果他们相遇,那么他们的距离的差一定是 L 的倍数,因为是个环。所以我们可以得到式子:X + Mt - Y - Nt = k * L,所以 (M - N) * t - k * L = Y - X。利用扩展欧几里德求解。

#include <cstdio>

using namespace std;

typedef long long LL;

long long N, M, x, y, L;

void init()
{
	scanf("%lld%lld%lld%lld%lld", &x, &y, &M, &N, &L);
}

LL gcd(LL a, LL b)
{
	if (!b) return a;
	return gcd(b, a % b);
}

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

void doit(void)
{
	LL a = M - N, b = L, c = y - x;
	LL g = gcd(a, b);
	if (c % g) { printf("Impossible\n"); return; }
	
	LL X, Y;
	ex_gcd(a, b, X, Y);
	X = X * c / g;
	X = (X % b + b) % b;
	if (!X) X = b;
	printf("%d\n", X);
	return;
}

int main(void)
{
	init(); doit();
	return 0;
}

2.poj2115.C Looooops

for (int i = a; i <= b; i += c, i %= 1 << k)能执行几次。死循环则输出 "FOREVER"

其实也就是求 (a + t * c) % (1 << k) = b % (1 << k),也就是t * c - l * (1 << k) = b - a。用扩欧求之。

#include <cstdio>

using namespace std;

typedef long long LL;

LL i, j, l, k;

LL gcd(LL a, LL b)
{
	return (b == 0) ? a : gcd(b, a % b);
}

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

void doit()
{
	LL a = l, c = j - i, b = 1;
	for (int p = 1; p <= k; p ++) b *= 2;  
	if (a < 0){
		a *= -1; b *= -1;
	}
	
	LL x, y, d = gcd(a, b);
	if (c % d != 0) { printf("FOREVER\n"); return; }
	
	a /= d; b /= d; c /= d;
	ex_gcd(a, b, x, y);
	x = (((x * c) % b) + b) % b;
	printf("%lld\n", x);
}

int main()
{
	while (scanf("%lld%lld%lld%lld", &i, &j, &l, &k) != EOF){
		if (i == 0 && j == 0 && l == 0 && k == 0) break;
		doit();
	}
	return 0;
}

3.bzoj1407.Savage(NOI2002)

一个岛上有若干洞穴排成一个环,有 N个野人,起初分别住在洞穴 Xi,每过一年,他们分别会向前走 Li (每个野人的Li 不同)并住下来,如果一个洞穴要住两个野人的时候,野人就会不高兴,从而使得小岛不安宁。但是令人惊奇的是,小岛从来没有过不安宁的情况,现在请你算出至少要多少个洞穴,才能使小岛永远和平。

很容易想到的算法是枚举 M,然后再用扩欧判断是否合法。但是 M10^6,算算复杂度感觉非常不靠谱。。大约 200000000,每个点0.5s 怎么跑得过?!但是网上的大神们好像都写得这种做法,我有点不明白。希望各位有人能解答我的疑问。

#include <cstdio>
#include <algorithm>
 
using namespace std;
 
const int MAX_N = 120;
 
int n, c[MAX_N], p[MAX_N], l[MAX_N];
int x, y, d, cnt = 0;
 
void init()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++) {
        scanf("%d%d%d", &c[i], &p[i], &l[i]),
        c[i] --,
        cnt = max(c[i], cnt);
    }
}
 
void ex_gcd(int a, int b, int &d, int &x, int &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;
    }
}
 
bool check(int m)
{
    for (int i = 1; i <= n; i ++)
        for (int j = i + 1; j <= n; j ++){
            int a = p[i] - p[j], b = m, C = c[j] - c[i];
            if (a < 0){
                a *= -1; C *= -1;     
            }
            ex_gcd(a, b, d, x, y);
            if (C % d) continue;
            int t = b / d;
            x = x * C / d;
            while (x < 0) x = x + t;
            while (x >= t) x = x - t;
            if (x <= l[i] && x <= l[j]) return 0;
        }
    return 1;
}
 
void doit()
{
    for (int i = cnt + 1; i <= 1000000; i ++)
        if (check(i)){ printf("%d\n", i); break; }
    return;
}
 
int main()
{
    init();
    doit();
    return 0;
} 







版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

扩展欧几里德算法的理解

  • 2012-09-07 21:33
  • 133KB
  • 下载

使用欧几里德距离构建简单的推荐系统计算用户相似度

在计算用户相似度的过程中,首先对于两个用户共同打分过的所有条目,计算他们对于每个条目的评分差值,对差值求平方、求和,再对结果求平方根,这样得到的值称为欧氏距离,但这并不足以作为显示度计算的度量值。相似...

扩展欧几里德算法的理解

  • 2011-03-02 10:01
  • 133KB
  • 下载

欧几里德算法及其简单应用----<acm数学>

欧几里德算法及其简单应用由最大公约数的几种求法入题,引出欧几里德算法,进而对欧几里德算法进行证明并简述其简单应用 1:欧几里德算法求解两个数的最大公约数 2:扩展欧几里德算法求解二元一次不定方程的...

扩展欧几里德问题

  • 2015-12-07 22:07
  • 65KB
  • 下载

扩展欧几里德(摘自JokerPark)

扩展欧几里得算法用于求解二元一次不定方程 ax+by=c 可以证明ax+by的最小正整数是gcd(a,b)。 设:ax+by=bx'+(a mod b)y'=gcd(a,b),  g=gcd(a...

扩展欧几里德定理

  • 2011-12-17 15:51
  • 81KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)