zoj 3868 GCD Expectation(数学)

本文介绍了一种计算整数集合所有非空子集的最大公约数(GCD)的k次方期望值的方法,并提供了一个具体的算法实现示例。该算法通过预处理和巧妙的数据结构实现了高效计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZOJ Problem Set - 3868
GCD Expectation

Time Limit: 4 Seconds      Memory Limit: 262144 KB

Edward has a set of n integers {a1a2,...,an}. He randomly picks a nonempty subset {x1x2,…,xm} (each nonempty subset has equal probability to be picked), and would like to know the expectation of [gcd(x1x2,…,xm)]k.

Note that gcd(x1x2,…,xm) is the greatest common divisor of {x1x2,…,xm}.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first line contains two integers nk (1 ≤ nk ≤ 106). The second line contains n integers a1a2,…,an (1 ≤ ai ≤ 106).

The sum of values max{ai} for all the test cases does not exceed 2000000.

Output

For each case, if the expectation is E, output a single integer denotes E · (2n - 1) modulo 998244353.

Sample Input
1
5 1
1 2 3 4 5
Sample Output
42

给出n k 给出n个数 求这n个数中所有非空子集最大公约数  期望值是gcd的k次方 最后用期望乘以 2^n-1
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <string.h>
#include <string>
#include <vector>
#include <queue>

#define MEM(a,x) memset(a,x,sizeof a)
#define eps 1e-8
#define MOD 998244353
#define MAXN 1000010
#define MAXM 100010
#define INF 99999999
#define ll long long
#define bug cout<<"here"<<endl
#define fread freopen("ceshi.txt","r",stdin)
#define fwrite freopen("out.txt","w",stdout)

using namespace std;

int Read()
{
    char ch;
    int a = 0;
    while((ch = getchar()) == ' ' | ch == '\n');
    a += ch - '0';
    while((ch = getchar()) != ' ' && ch != '\n')
    {
        a *= 10;
        a += ch - '0';
    }
    return a;
}

void Print(int a)    //Êä³öÍâ¹Ò
{
     if(a>9)
         Print(a/10);
     putchar(a%10+'0');
}

ll quick_mod(ll a,ll b)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=(ans*a)%MOD;
        a=(a*a)%MOD;
        b>>=1;
    }
    return ans;
}
ll a[MAXN];
ll two[MAXN],expe[MAXN];//expe记录期望值
void init()
{
    two[0]=1;
    for(int i=1;i<MAXN;i++)
        two[i]=(two[i-1]*2)%MOD;
}
int vis[MAXN],cnt[MAXN];

int main()
{
//    fread;
    init();
    int tc;
    scanf("%d",&tc);
    while(tc--)
    {
        ll n,k;
        scanf("%lld%lld",&n,&k);
        MEM(vis,0);
        MEM(cnt,0);
        ll mx=0;
        for(ll i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            if(!vis[a[i]])
            {
                vis[a[i]]=1;
                cnt[a[i]]=1;
            }
            else
                cnt[a[i]]++;
            mx=max(mx,a[i]);
        }
        expe[1]=1;
        for(ll i=2;i<=mx;i++)
            expe[i]=quick_mod(i,k);
        for(ll i=1;i<=mx;i++)
        {
            for(ll j=i+i;j<=mx;j+=i)
                expe[j]=(expe[j]-expe[i])%MOD;
        }
        ll ans=((two[n]-1)*expe[1])%MOD;
        for(ll i=2;i<=mx;i++)
        {
            ll num=0;
            for(ll j=i;j<=mx;j+=i)
            {
                if(vis[j]) num+=cnt[j];
            }
            ll x=((two[num]-1)*expe[i])%MOD;
            ans=(ans+x)%MOD;
//            cout<<i<<"  "<<ans<<endl;
        }
//        cout<<ans<<endl;
        printf("%lld\n",(ans+MOD)%MOD);
    }
    return 0;
}












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值