1299: 磊磊的难题
题目描述
最近磊磊迷上了coding,立志要做一个苦逼的程序员 ^o^ ,他遇到了这样一个神题:要求从小到大输出两个小数之间的所有分母不超过10w的最简分数。磊磊郁闷了, 他甚至都不知道这样的分数有多少个~ 于是他向你投来了哀怨的眼神……
输入要求
每组数据有5个数,a,b,c,d,n(其中 a <= b, c <= d 且 a*d <= b*c),且5个数字都不超过10万。
输出要求
输出 大于a/b 小于 c/d 且分母不超过 n 的最简分数的个数~ 输出占一行,处理到文件结尾。
假如输入
1 2 5 5 5 1 5 5 5 5
应当输出
4 8
提示
对于第一组数据 符合要求的最简分数有 2/3,3/4,3/5,4/5 答案为4
裸容斥,思路:
对于每一个分母确定分子的上下界,然后用容斥再求界内与分母互质的数,转换成小于上界与分母互质的数与小于下界与分母互质的数的个数差
TT,一开始上下界用的公式不是特别好。。wa40次TT
#include <cstdio>
#include <cmath>
using namespace std;
const double eps = 1e-6;
int n;
int aa[20], cnt;
long long dfs(int cur, int val)
{
long long res=0;
for (int i=cur; i<cnt; i++)
{
res += val/aa[i] - dfs(i+1, val/aa[i]);
}
return res;
}
void pnum(int tar)
{
int ok = 0;
while ((tar&1)==0)
{
ok = 1;
tar = (tar>>1);
}
if (ok)
{
aa[cnt++] = 2;
}
int i=3;
int limit = sqrt(tar)+1;
while (i<=limit && tar>1)
{
ok = 0;
while (tar%i==0)
{
ok = 1;
tar /= i;
}
if (ok)
{
aa[cnt++] = i;
}
i += 2;
}
if (tar>1) aa[cnt++] = tar;
}
int main()
{
int a, b, c, d;
while (scanf ("%d%d%d%d%d", &a, &b, &c, &d, &n) == 5)
{
long long res = 0, x, y;
for (int i=2; i<=n; i++)
{
x = 1.0*i*a/b+eps;
y = 1.0*i*c/d-eps;
if (x>=y) continue;
cnt = 0;pnum(i);
int tx = (y-dfs(0, y));
int ty = (x-dfs(0, x));
res += tx - ty;
}
printf ("%lld\n", res);
}
return 0;
}