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

来源:互联网 发布:macbookpro卸载软件 编辑:程序博客网 时间:2024/05/17 05:56

转载请注明出处,谢谢 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-7using 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;}


原创粉丝点击