Codeforces 839D Winter is here

链接:CF839D

题目大意

给定一个数组大小为\(n(1\leq n\leq 200000)\)的数组\(a\),满足\(1\leq a_i \leq 1000000\)

选择其中任意\(len\)个数字,若\(gcd>1\),则该组数字对答案贡献为\(len*gcd\),求最终答案对\(1e9+7\)取模。

题目分析

因为\(gcd\)的本质是因数分解,可以想到,如果不考虑算重,那么:

对于一个\(gcd\)的结果\(x\),若有\(num\)个数含有\(x\)这个因数,则被选择的数可形成的形如\(gcd(...)==x\)的贡献为:

\((1*C_{num}^1+2*C_{num}^2+3*C_{num}^3+......+num*C_{num}^{num})*x\)

其中,\(num\)可以直接枚举得到:

\(for(int\ i=x;i<=1000000;i+=x)num[x]+=cnt[i];\)

\(cnt[i]\)表示\(a[\ ]\)中大小为\(i\)的数字的个数,由于时间复杂度是调和级数,可以\(O(nlog(n))\)求解。

由公式可得:

\(1*C_{n}^1+2*C_{n}^2+3*C_{n}^3+......+n*C_{n}^{n}=n*2^{n-1}\)

此处可以\(O(1)\)求得答案,总时间复杂度为\(O(nlog(n))\)


由于会有重复的情况,我们可以使用容斥去重。

对于每个数的容斥系数,可以附初值:\(tmp[i]=i;\)

由于每个数会在它的因数部分算重,可得:

\(tmp[x]=x- \sum\limits_{d|x}tmp[d];\)

\(tmp\)数组的计算也是调和级数,可以在\(O(nlog(n))\)求解。


综上:

\(ans+=num[x]*2^{num[x]-1}*x*tmp[x];\)

总时间复杂度\(O(nlog(n))\)

代码实现

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cstdlib>
#define MAXN 0x7fffffff
typedef long long LL;
const int N=1000005,mod=1e9+7;
using namespace std;
inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
int tmp[N],prime[N];
bool vis[N];
void Pre(int n){
    for(int i=2;i<=n;i++)tmp[i]=i;
    for(int i=2;i<=n;i++){
        if(!vis[i])prime[++prime[0]]=i,vis[i]=1;
        for(int j=1;j<=prime[0]&&1ll*i*prime[j]<=n;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
        for(int j=2;1ll*i*j<=n;j++)tmp[j*i]-=tmp[i];
    }
} 
LL ksm(LL x,LL k){
    LL ret=1;
    while(k){
        if(k&1)ret=ret*x%mod;
        x=x*x%mod;
        k>>=1; 
    } 
    return ret;
}
int cnt[N];
int Query(int x){
    int ret=0;
    for(int i=x;i<=1000000;i+=x)ret+=cnt[i];
    return ret;
}
int main(){
    Pre(1000000);
    int n=Getint();
    for(int i=1;i<=n;i++)cnt[Getint()]++;
    LL ans=0;
    for(int i=2;i<=1000000;i++){
        int x=Query(i);
        if(!x)continue;
        ans=(ans+x*ksm(2,x-1)%mod*tmp[i]%mod)%mod;
    }
    cout<<(ans+mod)%mod;
    return 0;
}

转载于:https://www.cnblogs.com/Emiya-wjk/p/9991117.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值