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