COGS 513 八

513. 八

http://www.cogs.pro/cogs/problem/problem.php?pid=513

★☆   输入文件:eight.in   输出文件:eight.out   简单对比
时间限制:1 s   内存限制:128 MB

【问题描述】

八是个很有趣的数字啊。八=发,八八=爸爸,88=拜拜。当然最有趣的还是8用二进制表示是1000。怎么样,有趣吧。当然题目和这些都没有关系。 
某个人很无聊,他想找出[a,b]中能被8整除却不能被其他一些数整除的数。

【输入文件】

第一行一个数n,代表不能被整除的数的个数。第二行n个数,中间用空格隔开。第三行两个数a,b,中间一个空格。

【输出文件】

一个整数,为[a,b]间能被8整除却不能被那n个数整除的数的个数。

【样例输入】 
eight.in

3
7764 6082 462
2166 53442
【样例输出】

eight.out 
6378

【数据规模】 
对于30%的数据, 1≤n≤5,1≤a≤b≤100000。 
对于100%的数据,1≤n≤15,1≤a≤b≤10^9,N个数全都小于等于10^4大于等于1。

容斥原理

全集Z=能被8整除的数

性质p1为能被n1整除,p2为不能被8整除,能被n2整除……

ans=Z中不包含任意性质p的数

这些数都有一个前提:在Z中,所以最小公倍数初值为8

dfs过程中遇到的障碍:

求好几个数的lcm时,

没有必要for枚举几个数,在枚举哪几个数

容斥原理所有项的加加减减都是集合的所有子集

所以dfs时只需分这个数加不加入这个子集即可

这样计算n个数的lcm时,还可利用计算的n-1个数的lcm结果

递归式写法:

#include<cstdio>
using namespace std;
int n,ans;
long long a,b,f[10001];
int cal(long long m)
{
    return b/m-(a-1)/m;
}
long long gcd(long long x,long long y)
{
    return y ? gcd(y,x%y):x;
}
long long lcm(long long x,long long y)
{
    return x*y/gcd(x,y);
}
void dfs(int now,long long num,int sum)
{
    if(now==n+1)
    {
        int s=cal(num);
        if(sum%2) ans-=s;
        else ans+=s;
        return;
    }
    long long next=lcm(num,f[now]);
    dfs(now+1,next,sum+1);
    dfs(now+1,num,sum);
}
int main()
{
    freopen("eight.in","r",stdin);
    freopen("eight.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%lld",&f[i]);
    scanf("%lld%lld",&a,&b);
    dfs(1,8,0);
    printf("%d",ans);
}

非递归写法:

利用二进制生成子集

#include<cstdio>
using namespace std;
int n,ans;
long long a,b,f[10001];
int cal(long long m)
{
    return b/m-(a-1)/m;
}
long long gcd(long long x,long long y)
{
    return y ? gcd(y,x%y):x;
}
long long lcm(long long x,long long y)
{
    return x*y/gcd(x,y);
}
int main()
{
    freopen("eight.in","r",stdin);
    freopen("eight.out","w",stdout);
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%lld",&f[i]);
    scanf("%lld%lld",&a,&b);
    int sum=1<<n,cnt;
    ans=b/8-(a-1)/8;
    long long lcmm;
    for(int i=1;i<sum;i++)
    {
        cnt=0;lcmm=8;
        for(int j=0;j<n;j++) 
         if(i&(1<<j)) lcmm=lcm(lcmm,f[j]),cnt++;
        if(cnt&1) ans-=b/lcmm-(a-1)/lcmm;
        else ans+=b/lcmm-(a-1)/lcmm;
    }
    printf("%d",ans);
}
原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6623380.html