数学 exgcdbzoj1407: [Noi2002]Savage
Posted antiquality
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数学 exgcdbzoj1407: [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
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 ",m); 39 return 0; 40 } 41 return 0; 42 }
END
以上是关于数学 exgcdbzoj1407: [Noi2002]Savage的主要内容,如果未能解决你的问题,请参考以下文章
bzoj1407 [Noi2002]Savage——扩展欧几里得