容斥原理——状态压缩zoj3233 zoj2836升级版

zoj2836就是裸的求lcm进行容斥,用dfs比较直观

zoj3233增加了一个集合b,lcm(b)的倍数是不符合条件的

那么在zoj2836的基础上,把lcm(x,lcm(b))造成的影响减去即可

用状态压缩来枚举集合状况

/*
给定两个集合s1,s2 
求区间[L,R]能被s1里至少一个数整除,且不能被s2里至少一个数整除的个数 
lcm(s2)的所有倍数都不可行,所以当dfs遇到lcm==lcm(s2)时要反过来 
要防止越界的情况 
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long

const ll INF=1e18+100;

ll LCM,L,R,n,m,a[30],b[1000];

ll __lcm(ll a,ll b){//防溢出lcm 
    if(a==-1 || b==-1)return -1;
    ll d=__gcd(a,b);
    if(INF/b < a/d)return -1;//lcm越界了 
    return a/__gcd(a,b)*b;//顺序不能错 
}

ll query(ll x){//状态压缩集合解法 
    ll res=0;
    for(ll i=1;i<(1<<n);i++){//枚举每种状态集 
        ll num=1,cnt=0;
        for(ll j=0;j<n;j++)
            if(i & ((ll)1<<j))
                cnt++,num=__lcm(num,a[j+1]);//下标j偏移了一位 
        if(num==-1)num=INF;
        ll tmp=__lcm(num,LCM); 
        if(tmp==-1)tmp=INF; 
        if(cnt%2)res+=x/num-x/tmp; 
        else res-=x/num-x/tmp; 
    }
    return res;
}

int main(){
    while(cin>>n>>m>>L>>R,n){
        for(int i=1;i<=n;i++)cin>>a[i];
        for(int i=1;i<=m;i++)cin>>b[i];
        LCM=1;
        for(int i=1;i<=m;i++)LCM=__lcm(LCM,b[i]);    
        cout<<query(R)-query(L-1)<<endl; 
    }
}
原文地址:https://www.cnblogs.com/zsben991126/p/10862788.html