除法表达式
时间限制:1000 ms | 内存限制:65535 KB
难度:3
- 描述
给出一个这样的除法表达式:X1/X2/X3/···/Xk,其中Xi是正整数。除法表达式应当按照从左到右的顺序求和,例如表达式1/2/1/2的值为1/4。但是可以在表达式中嵌入括号以改变计算顺序,例如表达式(1/2)/(1/2)的值为1.
- 输入
- 首先输入一个N,表示有N组测试数据,
每组数据输入占一行,为一个除法表达式,
输入保证合法。
使表达式的值为整数。k<=10000,Xi<=100000000. - 输出
- 输出YES或NO
- 样例输入
1
1/2/1/2- 样例输出
- YES
(1)思路方向分析
不难知道整个表达式最后一定会变成这样的形式:A/B
而A,B都可以表达为这些数中某一部分的乘积。自然就会想到B越小越好,最好是1,这样就可以变成整数了~( ̄▽ ̄)"
经过一些尝试后会发现,X2必然在B中,而其他数都可以在上,这样问题就简单明了了
设 E=(XXXXX...X)/X 只需要判断E是否为整数即可!(ง •_•)ง
(2)方法
方一:
简单粗暴直接算,不会高精度的会溢出,会高精度的打码打到手抽(;´д`)ゞ
方二:
使用唯一分解定理,把X2拆了,然后看看剩下所有的Xi们能不能贡献出所需的pi对应的次数ai,这个方法很正确,但还可以更优化一点
方三:
没错就是直接约分~!每次把X2除掉与Xi的gcd,这样除k遍后如果X2是1那么E就是整数啦~(●ˇ∀ˇ●)
口以这么写:
bool judge(int *X) {
X[2] /= gcd(X[2],X[1]);
for(int i = 3;i <= k;i++) X[2] /= gcd(X[i],X[2]);
return X[2] == 1;
}
这个算法的关键是gcd,而这也是我学习这道题的主要目的。
推荐这个文章,写的真是太好了(´▽`ʃ♡ƪ),另外下方的评论也请不要错过!!
这个文章给出的最强gcd如下,O(log(max(a, b)))
int superGcd(int a,int b)
{
if(a==0)return b;
if(b==0)return a;
if(!(a&1)&&!(b&1))return superGcd(a>>1,b>>1)<<1;
else if(!(b&1))return superGcd(a,b>>1);
else if(!(a&1))return superGcd(a>>1,b);
else return superGcd(abs(a-b),min(a,b));
}
相关证明可以进一步的查看这里
而更相减损术则就是把x/y=n余c变成x-y=c同样都可以被z整除,所以把c与x或y放在一起求gcd都是不变的
我再说一遍,位移大法好啊啊啊啊啊
#include <cstdio>
using namespace std;
int n,X[10005],k;
char w;
int min(int a,int b)
{
if(a<b)return a;
else return b;
}
int abs(int a)
{
if(a<0)return 0-a;
else return a;
}
int gcd(int a,int b)
{
if(a==0)return b;
if(b==0)return a;
if(!(a&1)&&!(b&1))return gcd(a>>1,b>>1)<<1;
else if(!(b&1))return gcd(a,b>>1);
else if(!(a&1))return gcd(a>>1,b);
else return gcd(abs(a-b),min(a,b));
}
bool judge(int *X) {
X[2] /= gcd(X[2],X[1]);
for(int i = 3;i <= k;i++) X[2] /= gcd(X[i],X[2]);
return X[2] == 1;
}
int main()
{
scanf("%d",&n);
while(n--)
{
k=1;
scanf("%d",&X[1]);
w=getchar();
do{
scanf("%d",&X[++k]);
w=getchar();
}while(w=='/');
if(judge(X))printf("YES\n");
else printf("NO\n");
}
return 0;
}
所以以上为完整呆码~( ̄▽ ̄)"
顺带一提,如果你用欧几里得算法来求gcd,那么递归层数不会超过 4.785lgN+1.6723
我想我接下来就应该弄拓展欧几里得吧。。。
end——2018/3/9