[NOI2002] 荒岛野人 扩展欧几里得算法

Posted A_LEAF

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[NOI2002] 荒岛野人 扩展欧几里得算法相关的知识,希望对你有一定的参考价值。

【问题描述】

克里特岛以野人群居而著称。岛上有排列成环行的M个山洞。这些山洞顺时针编号为1,2,…,M。岛上住着N个野人,一开始依次住在山洞 C1,C2,…,CN中,以后每年,第i个野人会沿顺时针向前走Pi个洞住下来。每个野人i有一个寿命值Li,即生存的年数。下面四幅图描述了一个有6个 山洞,住有三个野人的岛上前四年的情况。三个野人初始的洞穴编号依次为1,2,3;每年要走过的洞穴数依次为3,7,2;寿命值依次为4,3,1。

技术分享 技术分享

技术分享 技术分享

奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?


【输入文件】

输入文件的第1行为一个整数N(1<=N<=15),即野人的数目。第2行到第N+1每行为三个整数Ci, Pi, Li (1<=Ci,Pi<=100, 0<=Li<=106 ),表示每个野人所住的初始洞穴编号,每年走过的洞穴数及寿命值。


【输出文件】

输出文件仅包含一个数M,即最少可能的山洞数。输入数据保证有解,且M不大于10^6

solution

(其实这个题根据 M不大于10^6 知道要枚举M)


我一开始思路是:

设当前总共的洞穴数是m个

对于每两个野人,如果他们会相遇,那么会有

(C[i]+k*P[i])≡(C[j]+k*P[j]) (%m)

即 m*t1=C[i]+k*P[i]...①  m*t2=C[j]+k*P[j]...②

①+②得   m*(t1+t2)-(P[i]+P[j])*k=C[i]+C[j]

可是这样没法解方程,就放弃了

 


 

看题解是:

对于野人i   (C[i]+k*P[i])%m=S1→C[i]+k*P[i]-k1*m=S1

对于野人j   (C[j]+k*P[j])%m=S2→C[j]+k*P[j]-k2*m=S2

相遇的条件是 S1==S2

所以两式相减  (P[i]-P[j])*k+x*m=C[j]-C[i]   (此时野人相遇S1==S2)

最后如果方程 无解 OR k>L[i]||k>L[j]  即为不相遇

 

技术分享
 1 #include<cstring>
 2 #include<cstdio>
 3 #include<iostream>
 4 #include<cstdlib>
 5 #define ll long long
 6 #define dd double
 7 #define mem(a,b) memset(a,b,sizeof(a))
 8 using namespace std;
 9 inline int maxn(int a,int b){return a>b?a:b;}
10 int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
11 
12 int n,be;
13 int C[21],p[21],L[21];
14 
15 void egcd(int a,int b,int &x,int &y)
16 {
17     if(b==0)
18     {
19         x=1;
20         y=0;
21         return ;
22     }
23     egcd(b,a%b,x,y);
24     int temp=x;
25     x=y;
26     y=temp-a/b*y;
27 }
28 
29 int check(int now)
30 {
31     int x,y,d,a,b,c;
32     for(int i=1;i<=n;++i)
33       for(int j=i+1;j<=n;++j)
34       {
35             a=p[i]-p[j];
36             b=now;
37             c=C[j]-C[i];
38             d=gcd(a,b);
39             
40             if(c%d!=0)
41               continue;
42             a/=d;
43             b/=d;
44             c/=d;
45             egcd(a,b,x,y);
46             b=abs(b);
47             x=((x*c)%b+b)%b;
48             if(x==0)
49               x+=b;
50             if(x<=min(L[i],L[j]))
51               return 0;
52         }
53     return 1;
54 }
55 
56 int main(){
57     
58     //freopen("savage.in","r",stdin);
59     //freopen("savage.out","w",stdout);
60     
61     //freopen("1.txt","r",stdin);
62     
63     scanf("%d",&n);
64     for(int i=1;i<=n;++i)
65     {
66       scanf("%d%d%d",&C[i],&p[i],&L[i]);
67         be=maxn(be,C[i]);
68     }
69     for(int i=be;i<=1000000;++i)
70       if(check(i))
71       {
72             cout<<i;
73             break;
74         }
75     //while(1);
76     return 0;
77 }
code

 

 

 


 最后再复习一下扩展欧几里得算法:

对于 ax+by=c

1.如果 c%gcd!=0,无解

2.求出gcd=gcd(a,b)   a/=gcd  b/=gcd  c/=gcd  先约分

3.egcd 解方程

4.x*c   y*c   是此方程一组解

5.如果要 求x,那么b=abs(b),x=x0+b*t(b已经约分,不用再/gcd)

  求y也一样

 

技术分享
 1 int gcd(int x,int y)
 2 {
 3     return y==0?x:gcd(y,x%y);
 4 }
 5 
 6 void egcd(int a,int b,int &x,int &y)
 7 {
 8     if(b==0)
 9     {
10         x=1;
11         y=0
12         return ;
13     }
14     int temp=x;
15     x=y;
16     y=temp-a/b*y;
17 }
18 
19 int cal(int a,int b,int c)
20 {
21     int x,y,d;
22     d=gcd(a,b);
23     if(c%d)
24       return -1;
25     a/=d;b/=d;c/=d;
26     egcd(a,b,x,y);
27     x=((c*x)%b+b)%b;
28     if(x==0)x+=b;
29     return x;
30 }
解方程

 



以上是关于[NOI2002] 荒岛野人 扩展欧几里得算法的主要内容,如果未能解决你的问题,请参考以下文章

[noi2002]荒岛野人 拓展欧几里得

BZOJ1407 NOI 2002 荒岛野人Savage

☆[noi2002]荒岛野人

BZOJ-1407: [Noi2002]Savage (扩展欧几里得)

cogs333 荒岛野人 扩展欧几里得

bzoj1407 [Noi2002]Savage——扩展欧几里得