TrickGCD (对于枚举gcd的另一种处理方法)

原题: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);
    }
}

猜你喜欢

转载自blog.csdn.net/jk_chen_acmer/article/details/82502615
今日推荐