作为一个oier,居然现在才弄扩展欧几里得,以前屡屡没有遇到,去年这个时候看了一点资料,但是没有写什么题目,后来就忘记了,这次终于弄了一下。
想想std用的居然还是一个颓颓的折半枚举>.<, 羞愧ing
题目化简后变成,解一个方程组中A的最小解
A = k1 * d1 + b1;
A = k2 * d2 + b2;
...
A = k20 * d20 + b20;
d1~20, b1~20 已知。
首先整理一下扩展欧几里得算法(尽量详细一点):
【1】 欧几里得算法: 由于gcd(a,b) = gcd(b, a%b) //a>= b, 每次递归或迭代处理。
【2】 求一组解 (x,y)满足:gcd(a,b)= a * x + b * y 的一组;
由于 a*x1 + b*y1 = gcd(a,b)
b*x2 + a%b*y2 = gcd(b,a%b)
所以 a*x1+b*y1 = b*x2 + a%b*y2 (考虑使用算数基本定理)
a*x1 + b*y1 = b*x2 + a*y2 - a/b * b*y2 (“/”是指整除)
合并得: a*x1 + b*y1 =a*y2 + b* (x2 - a/b*b*y2)
使用算数基本定理: x1 = y2 , y1 = x2 - a/b*b*y2;
所以在回溯的时候,利用x2,y2 得到 x1, y1;
【3】求x尽量小且为正整数的一组(x,y)呢?
化简 a/gcd(a,b) * x + b/gcd(a,b) * y = 1. 现在已知一组解(x1,y1)
x 每增加b/gcd(a,b), y减少a/gcd(a,b), 反之亦然。
另mo = b/gcd(a,b)那么min(x) = (x1 % mo + mo) % mo; 然后对应的y可以求出。
【4】 如果求a*x + b*y = c 的一组x最小正解呢?
如果c % gcd(a,b) != 0 则无解。
那么a/gcd(a,b) * x + b/ gcd(a,b) * y = c / gcd(a,b); 两边再同时处以c/gcd(a,b),
化为a*x + b*y = 1 的形式,得到一组(x1,y1), 然后x1*= c/gcd(a,b), y1 *= c/gcd(a,b),
由于a/gcd(a,b) + b/ gcd(a,b) 互质,仍然可以用上面的方法求出x为最小正整数的解(x,y)
【5】 回归原题,如果可以把20个式子化为1个,可以轻易地求出最小的A
将A = ki * di + bi, A = kj*dj + bj合并:
首先: di * ki - dj * kj = bj - bi
进一步用上面的方法化成 a*x - b*y = 1 的形式,只是如果把负数递归进gcd,必定会出问题,我们可以先求出 a * x + b * y = 1的一组解,
然后把y取反,在求x 的正最小解即可。
用上面的方法可以解出A(ki*di + bi)的一个正最小值, 可以知道,新获得的式子必定是A = lcm(ki , kj) *d+ b 的形式,A已知了,b自然可以得到,
这样两个式子就被合并成一个式子了。
poi2002 counting-out rhyme
给定约瑟夫环的出圈次序,求最小的k使得报k 的人出圈,出圈顺序与输入相符。
可以得到关于k 对于1,2,3,....,n的余数,轻松用上面的方法合并等式,求出最小的k:
# include <cstdlib> # include <cstdio> # include <cmath> using namespace std; typedef long long int64; int k, n,go[30], step[30], m[30]; int64 eb, k1, b1, d1, k2, b2, d2, k3, b3, d3, GCD; int find(int s, int t) { int now = s, ask = 0; for (;now !=t; ) { if (++now > n) now = 1; if (!step[now]) ask++; } return ask; } void prepare() { int now, i, c; for (now = go[1], m[n]= go[1], step[now]=true,i = n-1; i >= 1; i--) c = go[n-i+1], m[i] = find(now, c),now = c, step[now]=true; for (i = 1; i <= n; i++) m[i] %= i; } int64 gcd(int64 a, int64 b) {return !b?a:gcd(b,a%b);}; void ext_gcd(int64 a, int64 b, int64 &x, int64 &y) { if (!b) x = 1, y = 0; else { ext_gcd(b, a%b, x, y); int64 tmp = x; x = y; y = tmp - a/b * y; } } bool update(int64 d2, int64 b2) { eb = b2-b1; GCD = gcd(d1, d2); d1/= GCD; d2 /= GCD; if (eb % GCD) return false; else eb /= GCD; ext_gcd(d1, d2, k1, k2); k2 = -k2; k1 *= eb; k2 *= eb; k1 = (k1 % d2 + d2)% d2; k2 = (k1 * d1 - 1) / d2; d3 = d1 * d2 * GCD; b3 = (k1 * d1 * GCD + b1) % d3; d1 = d3; b1 = b3; } int main() { int i; scanf("%d",&n); for (i = 1; i <= n; i++) { scanf("%d", &k); go[k] = i; } prepare(); d1 = 1;b1 = 0; for (i = 2; i <= n; i++) if (!update(i,m[i])) break; if (i > n) printf("%I64d", b1); else printf("no"); return 0; }