扩展欧几里德算法 cogs.tk 2057. [ZLXOI2015]殉国
2057. [ZLXOI2015]殉国
★☆ 输入文件:BlackHawk.in
输出文件:BlackHawk.out
评测插件时间限制:0.05 s 内存限制:256 MB
【题目描述】
正义的萌军瞄准了位于南极洲的心灵控制器,为此我们打算用空袭摧毁心灵控制器,然而心灵控制器是如此强大,甚至能缓慢控制飞行员。一群勇敢的士(feng)兵(zi)决定投弹后自杀来避免心灵控制。然而自杀非常痛苦,所以萌军指挥官决定到达目的地后让飞机没油而坠落(也避免逃兵)。军官提供两种油:石油和中国输送来的地沟油,刚开始飞机没有油,飞机可以加几桶石油和几桶地沟油(假设石油和地沟油都有无限桶),飞机落地时必须把油耗尽,已知一桶石油和一桶地沟油所能支撑的飞行距离分别为a,b,驾驶员们必须飞往一个目的地,总距离为c.
1.最少,最多需要加几桶油,若只有一种方案,最少和最多的是相同的.
2.总共有多少种不同的加油配方(死法)能到达目的地。
【输入格式】
只有一行,三个正整数a,b,c
【输出格式】
两行,第一行为最少加几次油和最多加几次油,
第二行为加油方法总数。
若不存在任何方法,第一行输出-1 -1
第二行输出0
【样例输入】
样例1: 2 3 10 样例2: 6 8 10
【样例输出】
样例1: 4 5 2 样例2: -1 -1 0
【提示】
样例解释:
样例一:飞机加两次石油,两次地沟油,总次数为4,2*2+3*3=10
飞机加五次石油,不加地沟油,总次数为5,2*5+3*0=10
总共两种
样例二:飞机无法到达目的地
数据范围:
对于10%的数据,$a<=10^3,b<=10^3,c<=10^3$
对于20%的数据,$a<=10^4,b<=10^4,c<=10^6$
对于50%的数据,$a<=10^9,b<=10^9,c<=10^9$
对于100%数据,$a<=3·10^{18},b<=3·10^{18},c<=3·10^{18}$
三个答案分值权重分别为20%,30%,50%
1 /* 2 65分代码:寻找方法数和最大值和最小值,都是用的暴力,实在没想出其他的方法。 3 */ 4 #include<iostream> 5 using namespace std; 6 #include<cstdio> 7 typedef long long ll; 8 void exgcd(ll a,ll b,ll &x,ll &y,ll &gcd) 9 { 10 if(!b) 11 { 12 gcd=a;x=1;y=0; 13 return ; 14 } 15 exgcd(b,a%b,x,y,gcd); 16 ll t=x; 17 x=y; 18 y=t-(a/b)*y; 19 } 20 int main() 21 { 22 freopen("BlackHawk.in","r",stdin); 23 freopen("BlackHawk.out","w",stdout); 24 ll a,b,c,x,y,gcd,ans,minn,maxx; 25 cin>>a>>b>>c; 26 exgcd(a,b,x,y,gcd); 27 if(c%gcd) 28 { 29 minn=maxx=-1; 30 ans=0; 31 } 32 else 33 { 34 ll a0=a/gcd,b0=b/gcd; 35 ll k=c/gcd; 36 x*=k;y*=k; 37 ans=0; 38 if(x<0) 39 { 40 ll x1=-x; 41 x1/=b0; 42 x+=x1*b0; 43 y-=x1*a0; 44 if(x<0) x+=b0,y-=a0; 45 } 46 if(y<0) 47 { 48 ll y1=-y; 49 y1/=a0; 50 y+=y1*a0; 51 x-=y1*b0; 52 if(y<0) y+=a0,x-=b0; 53 } 54 minn=maxx=x+y; 55 ll x2=x,y2=y; 56 for(;y2>=0&&x2>=0&&x2<=c&&y2<=c;++ans) 57 { 58 minn=min(minn,x2+y2); 59 maxx=max(maxx,x2+y2); 60 x2+=b0; 61 y2-=a0; 62 } 63 for(;y>=0&&x>=0&&x<=c&&y<=c;++ans) 64 { 65 minn=min(minn,x+y); 66 maxx=max(maxx,x+y); 67 x-=b0;y+=a0; 68 } 69 --ans; 70 } 71 if(ans==0) minn=maxx=-1;// 72 cout<<minn<<" "<<maxx<<endl<<ans; 73 fclose(stdin); 74 fclose(stdout); 75 return 0; 76 }
网上的AC代码:
求exgcd,由xx=x*c/d+b/d*t≥0,yy=y*c/d−a/d*t≥0
得到t的区间[l,r]
方法数=r-l+1;
因为方程线性,所以最值在l、r取到
要写long double!!
1 #include<cstdio> 2 #include<cmath> 3 #define min(x,y) x<y?x:y 4 #define max(x,y) x>y?x:y 5 using namespace std; 6 long long a,b,c,d,x,y,xx,yy,ans1,ans2,ans; 7 void exgcd(long long a,long long b,long long &d,long long &x,long long &y) 8 { 9 if(!b) d=a,x=1,y=0; 10 else exgcd(b,a%b,d,y,x),y-=x*(a/b); 11 } 12 int main() 13 { 14 freopen("BlackHawk.in","r",stdin); 15 freopen("BlackHawk.out","w",stdout); 16 scanf("%lld%lld%lld",&a,&b,&c); 17 exgcd(a,b,d,x,y); 18 if(!(c%d)) 19 { 20 xx=ceil((long double)-x/b*c); 21 yy=floor((long double)y/a*c); 22 ans=yy-xx+1; 23 ans1=x*c/d+y*c/d+(b-a)/d*yy; 24 ans2=x*c/d+y*c/d+(b-a)/d*xx; 25 } 26 if(ans<=0) printf("-1 -1\n0"); 27 else printf("%lld %lld\n%lld",min(ans1,ans2),max(ans1,ans2),ans); 28 }