轉載請註明出處,謝謝 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;}