【拓展欧几里得】方程的解

【题目描述】
给出一个二元一次方程 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 long
using 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;
}
posted @ 2017-08-09 22:08  小蒟蒻ysn  阅读(319)  评论(0编辑  收藏  举报