1. 程式人生 > >【數學 exgcd】bzoj1407: [Noi2002]Savage

【數學 exgcd】bzoj1407: [Noi2002]Savage

保持 pan turn register image reg www esc 開始

exgcd解不定方程時候$abs()$不能亂加

Description

技術分享圖片

Input

第1行為一個整數N(1<=N<=15),即野人的數目。 第2行到第N+1每行為三個整數Ci, Pi, Li表示每個野人所住的初始洞穴編號,每年走過的洞穴數及壽命值。 (1<=Ci,Pi<=100, 0<=Li<=10^6 )

Output

僅包含一個數M,即最少可能的山洞數。輸入數據保證有解,且M不大於10^6。

Sample Input

3
1 3 4
2 7 3
3 2 1

Sample Output

6
//該樣例對應於題目描述中的例子。

題目分析

註意到$n$很小且保證$M≤10^6$,自然想到枚舉答案對不對!其實枚舉答案的復雜度是不對的但是就是可過(因為$n^2$的驗證大多數情況達不到上界;出題人的本意大概也是枚舉吧)

$O(M)$枚舉答案之後考慮如何驗證。兩個野人當總洞穴數為$M‘$時在有生之年相遇即$C_i+time*P_i≡C_j+time*P_j(modM‘)$,其中$time≤min\{L_i,L_j\}$。那麽這個式子就可以展開後作為不定方程求解了。註意最後要將特解變化為最小正整數解。

這裏要說的時,求解不定方程時,即使部分會出現負數情況,也不能夠亂加$abs()$

!因為符號的正負性在之後的方程中會被負負得正或保持符號。唯一要$abs()$的就是最後求最小正整數解時的變換。

哦這題枚舉答案還要從$mx$開始,因為有部分情況會出現$M‘<mx$在表達式上合法的情況。

 1 #include<bits/stdc++.h>
 2 
 3 int n,c[23],p[23],l[23],mx;
 4 
 5 void exgcd(int a, int b, int &x, int &y)
 6 {
 7     if (b==0){
 8         x = 1, y = 0;
 9         return
; 10 } 11 exgcd(b, a%b, y, x); 12 y -= a/b*x; 13 } 14 int gcd(int x, int y){return y==0?x:gcd(y, x%y);} 15 int abs(int x){return x>0?x:-x;} 16 bool able(int ts) 17 { 18 register int i,j,ci,cj,pi,pj,lt,d,mt; 19 for (i=1; i<=n; i++) 20 for (j=i+1; j<=n; j++) 21 { 22 int x,y; 23 ci = c[i], cj = c[j], pi = p[i], pj = p[j], lt = std::min(l[i], l[j]); 24 d = gcd(pi-pj, ts); 25 if ((cj-ci)%d) continue; 26 exgcd(pi-pj, ts, x, y); 27 mt = abs(ts/d), x = ((x*(cj-ci)/d)%mt+mt)%mt; 28 if (x <= lt) return 0; 29 } 30 return 1; 31 } 32 int main() 33 { 34 scanf("%d",&n); 35 for (int i=1; i<=n; i++) scanf("%d%d%d",&c[i],&p[i],&l[i]), mx = mx>c[i]?mx:c[i]; 36 for (int m=mx; m<=1000000; m++) 37 if (able(m)){ 38 printf("%d\n",m); 39 return 0; 40 } 41 return 0; 42 }

END

【數學 exgcd】bzoj1407: [Noi2002]Savage