【拓展欧几里得】方程的解
来源:互联网 发布:chrome linux版 编辑:程序博客网 时间:2024/05/30 13:41
【题目描述】
给出一个二元一次方程 ax+by=c,其中 x、y 是未知数,求它的正整数解的
数量。
【输入格式】
第一行一个整数 T,表示有 T 组数据。接下来 T 行,每行 3 个整数 a、b、c。
【输出格式】
输出 T 行,每行一个数,表示方程解的数量。如果正整数解的数量比
65535 还多,输出“ZenMeZheMeDuo”。
【样例输入】
3
-1 -1 -3
1 1 65536
1 1 65537
【样例输出】
2
65535
ZenMeZheMeDuo
【数据规模与约定】
20%的数据,a=b=1
40%的数据,T≤100,1≤a,b,c≤1000
另 20%的数据,a+b=c,1≤a,b,c≤1,000,000
另 20%的数据,1≤a,b,c≤1,000,000
100%的数据,T≤10000,-1,000,000≤a,b,c≤1,000,000
【题解】
核心为拓展欧几里得:
1、若a或b等于0看不为零的是否整除c 若c为0看a,b是否异号;
2、若两者为负就转化为一者为负;
3、若a,b异号,假设a<0,b>0则循环x至多b次;
4、若a,b同号但不与c同号,则方程无解 由此若a,b,c均为负就可以全部化为正;
以上为特殊情况,答案只可能是0或者无穷多。以下情况中,a,b,c均为正数。
5、枚举x,从1开始,满足c-ax>=0,寻找某一时刻(c-ax)%b==0,则找到了方程的一组解;
6、这一组解一定是x最小y最大的那组解,对于相邻的每两组解,y都减去了a和b的最小公倍数除以b,观察y能够减去多少个这个数;
再注意特判,注意处理负数情况即可。
#include<iostream>#include<cmath>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<vector>#define ll long long#define re register#define il inline#define fp(i,a,b) for(re int i=a;i<=b;i++)#define fq(i,a,b) for(re int i=a;i>=b;i--)using namespace std;il int gi(){ re int x=0; re short int t=1; re char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') t=-1,ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar(); return x*t;}void exgcd(int a,int b,int c,long long &x,long long &y,int &d)//拓展欧几里得算法模板{ if(!b) y=0,d=a,x=c/a; else exgcd(b,a%b,c,y,x,d),y-=(a/b)*x;}void f(){ int a,b,c,d; ll x,y; a=gi();b=gi();c=gi(); if(!a && !b)//特判a、b都等于0的情况 { if(!c) printf("ZenMeZheMeDuo\n"); else printf("0\n"); return; } if(c<0) a=-a,b=-b,c=-c; bool fana=0; if(a<0) a=-a,fana=1; bool fanb=0; if(b<0) b=-b,fanb=1;//处理掉负数,有负数会WA exgcd(a,b,c,x,y,d);//弄组解出来 if(a*x+b*y!=c)//一组解都没有就gg了 { printf("0\n"); return; } if(fana) x=-x,a=-a; if(fanb) y=-y,b=-b;//把负数弄回来 if(a==0) { if(y<=0) printf("0\n"); else printf("ZenMeZheMeDuo\n"); return; } if(b==0) { if(x<=0) printf("0\n"); else printf("ZenMeZheMeDuo\n"); return; } if(a<0 && b>0 || a>0 && b<0) { printf("ZenMeZheMeDuo\n"); return; }//特判很重要 if(a<0) a=-a,b=-b,c=-c; a/=d;b/=d;c/=d;//d是最大公约数 x%=b; if(x<=0) x+=b; if(x==0) x+=b;//保证模出的是正数 y=(c-a*x)/b; ll miny=y%a;//求出y的最小值 if(miny<0) miny+=a; if(miny==0) miny+=a; int res; if(miny>y) res=0; else res=(y-miny)/a+1;//求出解的个数 if(res>65535) printf("ZenMeZheMeDuo\n"); else printf("%d\n",res);}int main(){ freopen("fuction.in","r",stdin); freopen("fuction.out","w",stdout); int T; T=gi(); while(T--) f(); fclose(stdin); fclose(stdout); return 0;}
顺便再记一下爆搜+剪枝的方法,虽然慢,但不容易打错。。。
#include<cstdio>#include<algorithm>#define ll long longusing namespace std;ll T,a,b,c,neg;ll gcd(ll x,ll y){ if (y==0) return x; return gcd(y,x%y);}ll gi(){ ll x=0,w=1;char ch=getchar(); while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if (ch=='-') w=-1,ch=getchar(); while (ch>='0'&&ch<='9') { x=x*10+ch-'0'; ch=getchar(); } return x*w;} int main(){ //freopen("fuction.in","r",stdin); //freopen("fuction.out","w",stdout); T=gi(); while (T--) { a=gi();b=gi();c=gi();neg=0; if (a==0&&b==0&&c==0) { printf("ZenMeZheMeDuo\n"); continue; } if (c==0) { if (a==0||b==0) printf("0\n"); else if ((a<0&&b>0)||(a>0&&b<0)) printf("ZenMeZheMeDuo\n"); else printf("0\n"); continue; } if (a==0||b==0) { if (a==0&&b==0) printf("0\n"); else { ll t=(a==0)?b:a; if ((t>0&&c<0)||(t<0&&c>0)) printf("0\n"); else { if (t<0) t=-t,c=-c; if (c%t==0) printf("ZenMeZheMeDuo\n"); else printf("0\n"); } } continue; } if (a<0) neg++; if (b<0) neg++; if (c<0) neg++; if (a>0&&b>0&&c>0&&a+b==c) { printf("1\n"); continue; } if (neg>=2) a=-a,b=-b,c=-c,neg=3-neg; if (neg==1) { if (c<0) { printf("0\n"); continue; } if (a<0||b<0) { if (b<0) swap(a,b); bool key=0; for (ll x=1;x<=b;x++) if ((c-a*x)%b==0) { key=1;break; } if (key==1) printf("ZenMeZheMeDuo\n"); else printf("0\n"); continue; } } if (neg==0) { ll X,Y; for (X=1;c-a*X>0;X++) if ((c-a*X)%b==0) break; if (c-a*X<=0) printf("0\n"); else { Y=(c-a*X)/b; ll num=a/gcd(a,b); ll ans=Y/num+1; if (Y%num==0) ans--; if (ans>65535) printf("ZenMeZheMeDuo\n"); else printf("%lld\n",ans); } } } return 0;}
- 【拓展欧几里得】方程的解
- 【原创】用拓展欧几里得解不定方程 ※POJ-1061-青蛙的约会
- 拓展欧几里得解方程ax+by=gcd(a,b)
- 【原创】欧几里得算法与拓展欧几里得算法的证明及其应用(不定方程、逆元)
- 求解最大公约数——欧几里得算法及其(解同余方程)拓展欧几里得
- 拓展欧几里得算法求解同于方程
- 重拾拓展欧几里得 & 简单不定方程
- noip2012同余方程 (拓展欧几里得)
- 同余方程(拓展欧几里得模板)
- 扩展欧几里得求模方程的解
- 拓展的欧几里得算法
- 拓展的欧几里得
- 拓展的欧几里得算法
- 20140714 「初等数论 - 拓展欧几里得+同余模方程」POJ 1061 青蛙的约会
- 关于欧几里得算法和拓展欧几里德定理的证明(不定方程求解方法)
- [NOIP 2012] 同余方程 · 拓展欧几里得
- Wikioi 1200同余方程(拓展欧几里得算法)
- LuoguP1082[NOIP2012] 同余方程 解题报告【拓展欧几里得】
- 11-13springmvc实现json交互
- python小爬虫—抓取pixabay网站的图片资源
- C++向上转型
- 160个crackme之008
- 例题2
- 【拓展欧几里得】方程的解
- matlab求解全局最优(初步介绍)
- 用飞鱼星路由器上网时能联通VPN但不能连接到具体的服务器:
- 【jzoj5232】【NOIP2017模拟A组模拟8.5】【带权排序】【线段树】
- array_merge() 函数的用法
- 关于glibc的system函数调用实现
- three.js 开发中遇到的坑
- 用npm离线安装webpack
- 8-9 DAIRY