The 2021 China Collegiate Programming Contest (Harbin) CCPC哈尔滨

The 2021 China Collegiate Programming Contest (Harbin)

无情铁首,开最后一题的调试时间来不及只能说是实力不足…

D. Math master

time limit per test : 4 seconds
memory limit per test : 512 megabytes
inputstandard input
outputstandard output

Tang Keke is good at math. She knows how to simplify fractions very well. Even greater, she invented a simplifying method by herself!

The method is to choose some digits which appear in both the top and the bottom and erase them on both sides while keeping the fraction value unchanged. The digits are the same, so they can reduce each other. Sounds very logical, right?

The method may produce multiple simplified results and the one with the least top number is called the most simplified form. Keke prepared some fractions and is going to test if you can get the most simplified form.

Note that the chosen digits form a multiset, you can see the examples for more details.

Input
The first line contains an integer n (1 ≤ n ≤ 10), denoting the number of fractions.

Each of the next n lines contains two integer p,q (0 < p,q <2^63), denoting the fraction p/q.

Output
For each fraction, output two integers x,y in one line, indicating the most simplified form of the corresponding fraction is x/y.

Notice: if there are some leading zeros after removal, you can ignore them and output the normal number. For example, if you get 007/123 finally, then you should output “7 123” instead of “007 123”.

Example

input

4
163 326
326 163
1000 1000
2232 162936

output

1 2
2 1
1 1
232 16936

Note

  • For the first and the second case, the erased digit multisets are both {3,6}.
  • For the third case, the erased digit multiset is {0,0,0}.
  • For the fourth case, the erased digit multiset is {2}.

Code

#include<bits/stdc++.h>
using namespace std;
//必须开__int128 否则在计算分母时1e18*1e18会炸long long
inline __int128 read()
{
    __int128 x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline void write(__int128 x)
{
    if(x<0)
    {
        putchar('-');
        x=-x;
    }
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
__int128 qpow(__int128 a,__int128 x)
{
    __int128 sum=a,ans=1;
    while(x>0)
    {
        if(x%2==1)ans*=sum;
        sum*=sum;
        x>>=1;
    }
    return ans;
}//可预处理10的幂次 这里傻逼了不过影响不大
int n;
int av[11],bv[11];
__int128 a,b,mn;
map<__int128,int> mp;//因 Key类型为__int128故不使用unorded_map
int u[11];
int mx;
__int128 aa,ab,md,ya,yb;
__int128 ck(__int128 x,__int128 n,int d)//返回 x 删右起第 d 个 n
{
    int z=0;
    __int128 sum=0,sum1=0;
    __int128 cd=0;
    while(x>0)
    {
        ++cd;
        md=x%10;
        x/=10;
        if(md==n)++z;
        if(z<d)
        {
            sum1=sum1+md*qpow(10,cd-1);
        }
        if(z==d)break;
    }
    sum=x*qpow(10,cd-1);
    return sum+sum1;
}
int jd(__int128 x)
{
    if(x*yb%ya!=0)return 0;//如删后的 a 对应的分母不为整数,既不合法
    else
    {
        __int128 bp=x*yb/ya;
        int at[11]={0},ct[11]={0};//ct桶存储删掉的数
        queue<int> st;//使用堆保证留下的数的顺序与原数顺序能进行判断
        while (!st.empty())st.pop();
        __int128 md;
        __int128 lya=ya,lyb=yb;
        while(x>0)
        {
            md=x%10;
            ++at[md];
            x/=10;
        }
        while(lya>0)
        {
            md=lya%10;
            if(at[md]>0)
            {
                --at[md];
            }
            else
            {
                ++ct[md];
            }
            lya/=10;
        }
        while(bp>0)
        {
            md=bp%10;
            st.push(md);
            bp/=10;
        }
        st.push(-1);//空堆的front()==0会对判断进行干扰 于是在堆尾推个 -1
        int f=1,yl=0;//如高位的数删除后前导0会自动删除 故记忆前导零的情况
        while(lyb>0)
        {
            md=lyb%10;
            if(st.front()==md)
            {
                if(yl==1)return 0;//如前导零存在但前数未被删除既不合法
                st.pop();
            }
            else if(ct[md]>0)--ct[md];
            else if(md==0)
            {
                yl=1;//记忆前导零情况
            }
            else return 0;
            lyb/=10;
        }
        if(st.front()!=-1)f=0;//堆若为非空既不合法
        if(f==1)
        {
            for(int i=0;i<=9;++i)
            {
                if(ct[i]!=0)//若删除的数未全部用上亦不合法
                {
                    f=0;
                    break;
                }
            }
        }
        return f;
    }
}
void dfs(__int128 a,int s)
{
    if(mp[a]!=0)return;
    mp[a]=1;//避免处理重复数字
    if(jd(a)==1)//判断处理出来的 a 对应的分母是否合法
    {
        if(a<aa)//为符合题意中留下 分子最小 的解
        {
            aa=a;
            ab=a*yb/ya;
        }
    }
    if(s==mn-1)return;//个位数的情况不能再继续处理
    for(int i=0;i<=9;++i)//枚举删哪个数
    {
        if(av[i]==0||bv[i]==0)continue;
        else if(u[i]<av[i]&&u[i]<bv[i])
        {
            int ag=av[i]-u[i];
            for(int j=1;j<=ag;++j)
            {
                ++u[i];
                dfs(ck(a,i,j),s+1);//枚举删右起第 j 个 i
                --u[i];
            }
        }
    }
    return;
}
int main()
{
    scanf("%d",&n);
    while(n--)
    {
        mp.clear();
        mx=0;
        a=read();
        b=read();
        for(int i=0;i<=9;++i)
        {
            av[i]=0;
            bv[i]=0;
            u[i]=0;
        }
        __int128 ac=0,bc=0;
        __int128 awl=0,bwl=0,na=a,nb=b;
        while(na>0)
        {
            md=na%10;
            na/=10;
            if(md!=0)break;
            ++awl;
        }
        while(nb>0)
        {
            md=nb%10;
            nb/=10;
            if(md!=0)break;
            ++bwl;
        }
        mn=min(awl,bwl);
        __int128 cl=qpow(10,mn);
        a/=cl;
        b/=cl;//处理两数尾端共同数量的 0
        na=a;
        nb=b;
        while(na>0)
        {
            ++ac;
            na/=10;
        }
        while(nb>0)
        {
            ++bc;
            nb/=10;
        }
        if(ac<=1||bc<=1)
        {
            write(a);
            printf(" ");
            write(b);
            puts("");
            continue;
        }
        mn=min(ac,bc);//得到两数长度的最小值
        __int128 la=a,lb=b;
        aa=a;
        ab=b;
        while(la>0)
        {
            md=la%10;
            la/=10;
            ++av[md];
        }
        while(lb>0)
        {
            md=lb%10;
            lb/=10;
            ++bv[md];
        }//统计 a,b 两数中各数字出现的次数
        ya=a;
        yb=b;
        for(int i=0;i<=9;++i)
        {
            if(av[i]!=0&&bv[i]!=0)
            {
                dfs(a,0);
                break;
            }
        }
        write(aa);
        printf(" ");
        write(ab);
        puts("");
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值