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;
}