Code Forces 585 C. Alice, Bob, Oranges and Apples(数论)

Description
Alice和Bob两个人遇见一袋子橘子和一袋子苹果,首先Alice拿了一个橘子,Bob拿了一个苹果,之后有两种操作,A操作,Alice将手中所有水果都给Bob,然后从袋子中拿和原先相同的水果,B操作,Bob将手中所有的水果都给Alice,然后从袋子中拿和原先相同的水果,现在给出两袋子中橘子和苹果的数量x和y,问两人经过一定的操作后能否恰好拿完所有水果,如果可以输出操作序列,否则输出Impossible
Input
两个整数x和y表示橘子数和苹果数
Output
如果可以拿完所有水果则输出操作序列(注意这个序列是简写形式,例如,AAAB要写成3A1B),如果不可以则输出Impossible
Sample Input
3 2
Sample Output
1A1B
Solution
首先介绍一个树形结构Stern-Brocot Tree,其结构如下图
Code Forces 585 C. Alice, Bob, Oranges and Apples(数论)_第1张图片
可以看出每次可以用相邻两个分数a/b和c/d构造出下一层树处于这两个分数中间的分数(a+c)/(b+d),而这相邻两个数必然满足b*c-a*d=1,且任何一个分数的分子和分母互素,这两个结论可以由数学归纳法证之,那么从这棵树的性质中我们可以发现,通过不停的往下构造这棵树一定可以不重复的表示所有的有理数
再看这道题,如果将两个人的橘子数/苹果数分别看作两个分数(a/b,c/d),初始状态就是(1/0,0/1),那么每次操作就是将两个分数(a/c,b/d)变为((a+c)/(b+d),b/d)或者(a/c,(a+c)/(b+d)),如果存在满足条件的操作序列那么最后一步就是由两个分数得到x/y,故问题转化为从树上找一条到达x/y的路径,显然如果gcd(x,y)!=1这种路径不存在,输出impossible,而如果gcd(x,y)=1那么路径一定存在,考虑如果从第一层的(1/0,0/1)找到路径到x/y,如果x > y,那么往右子树走到(x-y)/y,一直走到(x%y)/y,这个过程就是进行A操作x/y次;如果x < y,那么往左子树走到x/(y-x),一直走到x/(y%x),这个过程就是进行B操作y/x次。所以在用欧几里得算法求gcd(x,y)时即可记录一个合法的操作序列,时间复杂度O(log(max(x,y)))
Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
ll x,y,ans[111111];
int res;
ll gcd(ll x,ll y)
{
    if(!y)return x;
    ans[res++]=x/y;//记录每次的操作数 
    return gcd(y,x%y);
}
int main()
{
    char a,b;
    while(~scanf("%I64d%I64d",&x,&y))
    {
        a='A',b='B';
        res=0;
        if(x<y)//为操作方便保证每次x>y 
            swap(x,y),swap(a,b);
        if(gcd(x,y)!=1)//不合法情况 
            printf("Impossible");
        else
            for(int i=0;i<res;i++,swap(a,b))//每次操作都会变化 
                printf("%I64d%c",(i==res-1?ans[i]-1:ans[i]),a);
        printf("\n");   
    }
    return 0;
}

你可能感兴趣的:(Code Forces 585 C. Alice, Bob, Oranges and Apples(数论))