之江学院第0届校赛 qwb与支教 (容斥公式)

description

qwb同时也是是之江学院的志愿者,暑期要前往周边地区支教,为了提高小学生的数学水平。她把小学生排成一排,从左至右从1开始依次往上报数。

玩完一轮后,他发现这个游戏太简单了。于是他选了3个不同的数x,y,z;从1依次往上开始报数,遇到x的倍数、y的倍数或z的倍数就跳过。如果x=2,y=3,z=5;第一名小学生报1,第2名得跳过2、3、4、5、6,报7;第3名得跳过8、9、10,报11。

那么问题来了,请你来计算,第N名学生报的数字是多少?

input

多组测试数据,处理到文件结束。(测试数据数量<=8000)

每个测试例一行,每行有四个整数x,y,z,N。( 2≤x,y,z≤107,1≤N≤1017)。

Output

对于每个测试例,输出第N名学生所报的数字,每个报数占一行。

Sample Input

2 3 5 2
6 2 4 10000

Sample Output

7
19999

分析:

这道题很关键的一点就是如何确u是需要跳过去的,因为数据的范围非常的大,暴力的话肯定是不现实的。我们可以通过容斥定理计算出1-q之间有多少个数会被报到(不是x,y,z任何一个数的倍数),直接计算不是其倍数的数不好计算。可以计算出是x,y,z中一个数的倍数的数的个数。计算公式为S=q/x+q/y+q/z-q/gcd(x,y)-q/gcd(y,z)-q/gcd(z,x)+q/gcd(x,y,z);q-S就是报到的数

然后我们可以通过二分的方式找出最早报出N个数的q值,就是本题的答案。

代码:

#include<bits/stdc++.h>
#define SI(x) scanf("%d",&x)
using namespace std;
typedef long long LL;
LL xy,yz,xz,xyz;///用来保存相应的最小公倍数
LL x,y,z,N;
LL low,high,mid;///二分查找法的左右端点,以及中间点
LL gcd(LL a,LL b)///求公约数的函数
{
    return b==0? a:gcd(b,a%b);
}

void Binary()
{
    low = 0;
    high = 1e18;
    while(low+1<high)
    {

        mid = (low+high)/2;
        cout<<"low==="<<low<<"  high=="<<high<<"  mid=="<<mid<<endl;
        LL ccount = mid/x+mid/y+mid/z-mid/xy-mid/yz-mid/xz+mid/xyz;///表示的是需要跳过去的数字个数
        if(mid-ccount>=N) high=mid;///跳过的数字和N的和比mid大,肯定在左区间找
        else  low=mid;///否则在右区间找
    }
}
int main()
{
    while(~scanf("%lld%lld%lld%lld",&x,&y,&z,&N))
    {
        xy = (x*y)/gcd(x,y);
        xz = (x*z)/gcd(x,z);
        yz = (y*z)/gcd(y,z);
        xyz = (xy*z)/gcd(xy,z);
        Binary();
        printf("%lld
",high);
         printf("%lld
",low);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/cmmdc/p/6940775.html