【数学 exgcd】bzoj1407: [Noi2002]Savage

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

posted @ 2018-07-25 18:02  AntiQuality  阅读(255)  评论(0编辑  收藏  举报