ZOJ 3233 Lucky Number(数论,容斥原理)

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

题目:给出两组数,要求在区间内找出有多少个数,满足至少能被第一组中的一个数整除,而且至少能不被第二组中的一个数整除。

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3490

开始题目看错了,以为是不能被第二组中的任何一个数整除,这样应该更难处理了。

我们定义第一个条件为P1,第二个条件为P2。

则要求的是P1&&P2。由于P2条件比较奇怪,至少不能被一个数整除,所以我们考虑其反命题,能被第二组中的所有数整除,则是能被最小公倍数整除。

那么P1&&P2=P1-P1&&(~P2),这样便可以解决了。

我调用的容斥次数太多,太耗时了,可以在一次调用中全部解决。请读者自行思考

另外就是注意溢出神马的。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<string>
#include<algorithm>
#include<queue>
#define LL long long
#define eps 1e-7
using namespace std;
vector<LL>b;
int cbln,cbun;
LL l,r,k;
LL ret,bun;
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
void dfs(int k,int remain,int idx,int cnt,LL num,LL n){
    if(remain==0){
        if(k&1)
            ret+=n/num;
        else
            ret-=n/num;
        return ;
    }
    if(idx>=cnt)
        return ;
    dfs(k,remain,idx+1,cnt,num,n);
    LL tmp=num/gcd(num,b[idx])*b[idx];
    if(tmp<=n&&tmp>0)
        dfs(k,remain-1,idx+1,cnt,tmp,n);
}
LL slove(LL up,int cnt,LL init){
    if(up<=1) return 0;
    ret=0;
    for(int i=1;i<=cnt;i++)
        dfs(i,i,0,cnt,init,up);
    return ret;
}
int main(){
    while(scanf("%d%d%lld%lld",&cbln,&cbun,&l,&r)!=EOF){
        if(cbln+cbun+l+r==0)  break;
        b.clear();
        for(int i=0;i<cbln;i++){
            scanf("%lld",&k);
            b.push_back((LL)k);
        }
        bun=1;
        bool flag=false;
        LL a=slove(r,cbln,1LL)-slove(l-1,cbln,1LL);
        for(int i=0;i<cbun;i++){
            scanf("%lld",&k);
            if(i==0) bun=k;
            else{
                bun=bun/gcd(bun,k)*k;
                if(bun<0)  flag=true;
            }
        }
        if(flag){
            printf("%lld\n",a);
            continue;
        }
        LL b=slove(r,cbln,bun)-slove(l-1,cbln,bun);
        printf("%lld\n",a-b);
    }
    return 0;
}




你可能感兴趣的:(UP)