作爲一個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;
}