zoj3868

来源:互联网 发布:淘宝卖家真实姓名隐藏 编辑:程序博客网 时间:2024/06/07 18:53



/***    题目:I.GCD Expectation    链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5480    题意:给定一个可以重复的集合,对于所有非空的子集,计算其gcd的k次方的和模998244353(题意理解之后就是这样的意思),sum{gcd(ai,aj……)^k}%998244353;    思路:从大到小枚举可能的gcd,假如我们枚举到了i,那么集合里面类似i,2*i,3*i,4*i这样的数存在的话都是可以统计进来的。         假如这样的数是num,那么可能的集合个数就是2^num-1,然后减去gcd不是i的就是gcd是i的个数了,不是i的记录在dp数组里面,详见代码!复杂度O(Nlog(N)),其中N=max{ai}    代码:***/#include<map>#include<queue>#include<cmath>#include<cstdio>#include<vector>#include<cstring>#include <iostream>#include<algorithm>using namespace std;const int N=2000010;const long long MOD=998244353;long long dp[N],vis[N];int n,k,a[N];long long q_pow(long long x,int y){    long long res=1;    while(y>0){        if(y&1)res=(res*x)%MOD;        y>>=1;        x=(x*x)%MOD;    }    return res;}int main(){    int T;    scanf("%d",&T);    while(T--){        scanf("%d%d",&n,&k);        int maxn=0;        for(int i=0;i<n;i++){            scanf("%d",&a[i]);            maxn=max(maxn,a[i]);        }        long long res=0;        for(int i=0;i<=maxn;i++)            vis[i]=dp[i]=0;///初始化        for(int i=0;i<n;i++)            vis[a[i]]++;///记录个数        for(int i=maxn;i>=1;i--){            int num=vis[i];///这里初始化成vis[i]可以应对给定的集合是重复的            long long sum=0;            for(int j=i+i;j<=maxn;j+=i){                sum=(sum+dp[j])%MOD;///最大公约数都不是i的集合个数,且组成这个集合的元素是i的倍数                num+=vis[j];            }            long long now=q_pow(2,num)-1;///非空的集合个数            ///减去那些最大公约数不是i的个数sum,然后剩下最大公约数是i的个数dp[i]            dp[i]=((now-sum)%MOD+MOD)%MOD;            res=(res+q_pow(i,k)*dp[i])%MOD;///gcd的k次方乘以个数        }        cout<<res<<endl;    }    return 0;}

0 0