ABC195 F - Coprime Present【gcd+状压DP】

题意

求出由 \([A,B]\) 内数字组成的内部数字两两互质的集合的个数。\((1≤A≤B≤10^{18})\)题目链接:https://atcoder.jp/contests/abc195/tasks/abc195_f

分析

首先,根据辗转相除法,有 \(gcd(a,b)=gcd(a-b,b)(a\geq b)\),因此这些数字的 \(gcd\) 的范围不会超过 \(72\),只需要对这些质数进行讨论就可以判断两个数是否互质。

因为 \(72\)以内的质数只有 \(20\) 个,因此可以通过状态压缩来表示每个数含有的质数的情况。当两个状态相与结果为 \(0\) ,表示二者互质。dp[i] 表示 i 对应的集合个数。

代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N=(1<<20);
bool vis[80];
int prime[25],cnt;
ll dp[N];
void init(){
    int maxn=72;
    cnt=0;
    for(int i=2;i<=maxn;i++){
        if(!vis[i])
            prime[++cnt]=i;
        for(int j=1;j<=cnt&&prime[j]*i<=maxn;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
}
int main(){
    init();
    ll a,b;
    scanf("%lld%lld",&a,&b);
    dp[0]=1;//空集
    for(ll i=a;i<=b;i++){
        int msk=0;
        for(int j=1;j<=cnt;j++){//得到i含有的质因子
            if(i%prime[j]==0)
                msk|=(1<<(j-1));
        }
        for(int j=0;j<(1<<cnt);j++){
            if(!(msk&j))//表示两个状态互质
                dp[msk|j]+=dp[j];
        }
    }
    ll ans=0;
    for(int i=0;i<(1<<cnt);i++) 
        ans+=dp[i];
    printf("%lld\n",ans);
    return 0;
}
posted @ 2021-03-16 19:27  xzx9  阅读(79)  评论(0编辑  收藏  举报