辗转相除的思想与运用:
先来道比较水的题目:(spoj 3899)
题目的大意是:给出a,b,x,y,求出最小的q使得 a/b < p/q < x/y,若有多解则求p最小的。
咋一眼看上去没有什么思路。
其实我们可以注意到:y/x < q/p < b/a,也就是说,我们取倒数的话对不等式没有什么影响。
那么我们设[a/b] = k,我们对于每个分数都减去k
我们得出来a',p',x',取倒数后递归解决y/x' < q/p' < b/a'
最后假如当前的a/b < 1,x/y > 1,那么q直接取1就好了。
求出p'之后,q = [p'y/x'] + 1
#include
#include
using namespace std;
typedef long long LL;
LL dfs(LL a,LL b,LL c,LL d)
{
int i = a / b;
a -= b * i,c -= d * i;
if (c > d) return 1;
if (!a) return d / c + 1;
LL p = dfs(d,c,b,a);
return p * d / c + 1;
}
int main()
{
int a,b,c,d;
while (scanf("%d %d %d %d", &a, &b, &c, &d) != EOF)
{
LL q = dfs(a,b,c,d);
printf("%lld/%lld\n",q * a / b + 1,q);
}
}
然后一道没这么水的(我觉得而已-_-) (spoj 4717)
题目其实就是给出n,a,b,C,求出sigma(x=0->n)[(ax+C)/b]
是不是觉得很水
我们可以进行一些变形
当a<b时,
设Y = [ax/b + C],则原式=(sigma(x=0->n)sigma(y=0->Y-1)(y<Y));
交换x,y的位置,原式=(sigma(y=0->Y-1)sigma(x=0->n)(y<Y)
观察当y<Y,时,y < [ax/b + C] -> y + 1 <= [ax/b + C],y + 1 - C <= [ax/b],(y+1-C)*b < [ax]
[(y+1-C)*b-1) / a] < x.
原式=sigma(y=0->Y-1)(n - [((y+1-C)*b-1)/a])
我们成功地将a,b互换了位置!!!然后我们就可以递归求解。
当a>= b时,
原式=sigma(x=0->n)([a/b]x) + sigma(x=0->n)([c/b])+sigma(x=0->n)([((a%b)x+c%b)/b]);
我们继续递归求sigma(x=0->n)([((a%b)x+c%b)/b])
问题就完美解决了。。。
#include
#include
#include
using namespace std;
typedef long long LL;
void read(int &x)
{
char c;
while (c = getchar(),c < '0' || c > '9');
x = c - 48;
while (c = getchar(),c >= '0' && c <= '9') x = x * 10 + c - 48;
}
int gcd(int a,int b) {return b ? gcd(b,a % b) : a;}
LL Dfs(LL n,LL a,LL b,LL c)
{
if (!a) return (c / b) * LL(n + 1);
if (n < 0) return 0;
if (a >= b)
{
LL tmp = (a / b) * (LL(n) * (n + 1) / 2) + Dfs(n,a % b,b,c % b) + (c / b) * LL(n + 1);
return tmp;
} else
{
int Y = (LL(n) * a + c) / b;
return LL(Y) * n - Dfs(Y - 1,b,a,b - 1 - c);
}
}
void Work()
{
int n,a,b;read(n),read(a),read(b);
int k = gcd(a,b);a /= k,b /= k;
printf("%lld\n",Dfs(n,a,b,0) + n + 1);
}
int main()
{
int T;read(T);
for(;T;T --) Work();
return 0;
}