原题:hdu 6053
题意:
给出每个位置的上限,求所有数gcd大于等于2的方案数、
解析:
首先,这种题目一定是枚举每个gcd开局的,然后是上限的处理。
假设gcd=10,那么10~19对于ans的贡献都是1,20~29对ans的贡献都是2,所以我们可以用前缀和记录一下某个上限区间内的数的个数,再枚举x的情况得出gcd=kx的方案数,最后反向容斥得出gcd=x的方案数
gcd后宫又添新宠!!!
代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL mod=1e9+7;
const int N=100009;
LL sw(LL a,LL b){
LL ans=1;
while(b){
if(b&1)ans=ans*a%mod;
a=a*a%mod;b>>=1;
}return ans;
}
int sum[2*N];int n;
LL dp[N];
int main(){
int t;scanf("%d",&t);int ca=0;
while(t--){
scanf("%d",&n);
memset(sum,0,sizeof(sum));
memset(dp,0,sizeof(dp));
int lim=1000000;
for(int i=1;i<=n;i++){
int tmp;
scanf("%d",&tmp);lim=min(lim,tmp);
sum[tmp]++;//前缀和数组
}
for(int i=1;i<=200001;i++)sum[i]+=sum[i-1];
for(int i=2;i<=lim;i++){
LL ans=1;
for(int j=1;;j++){
int s=sum[i*j+i-1]-sum[i*j-1];//区间内的个数
int br=0;
if(sum[i*j+i-1]==sum[200001])br=1;//后面没有数了
if(s){
ans=ans*sw(j,s)%mod;
}
if(br)break;
}
dp[i]=ans;
}
LL ans=0;
for(int i=lim;i>=2;i--){
for(int j=i+i;j<=lim;j+=i){
dp[i]-=dp[j];//容斥
}
dp[i]=(dp[i]%mod+mod)%mod;
ans+=dp[i];
}
ans%=mod;
printf("Case #%d: %lld\n",++ca,ans);
}
}