hdu5321 beautiful set 莫比乌斯反演

题意:给定一个集合A{1…n},计算集合的美丽值(mod 258280327):

Mr.Zstu的算法:将集合变成序列,一个序列的美丽值是这个序列所有区间GCD之和,一个集合的美丽值是这个集合所有可能序列的美丽值之和。例如:集合{1,2,3}可以是6种不同的序列:{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,1,2},{3,2,1}。

Mr.Hdu的算法:对于k(取值为1~n),从集合中选择k个数字,计算它们的GCD。k个数字的美丽值是k*GCD。一个集合的美丽值是所有k个数字的美丽值之和。


题解:

nlogn预处理出1~100000的因子。

nlogn预处理出1~100000的莫比乌斯系数。

对于集合A,求出cnt[i](表示集合中 i 的倍数的个数)

对于ZSTU:

       F1[ x ]表示gcd为x的倍数的区间的个数;

       f 1[ x ]表示gcd为x的区间的个数;

           F[ i ] = sigma(A(cnt[i],j)* (n-j+1)!)   (先选出j个元素的排列,然后捆绑法计算总数 )

对于HDU:

       F2[ x ]表示gcd为x的倍数的选法的系数;

       f 2[ x ]表示gcd为x的选法的系数;

           F2[ i ]=sigma(k* c(cnt[i],k))=cnt[i] * (2^(cnt[i]-1))

直接套莫比乌斯反演,计算 f [ ] ,ans=sigma(i * f [ i ])


知识积累:  莫比乌斯反演    链接  http://blog.csdn.net/acdreamers/article/details/8542292

莫比乌斯反演的两种形式:

    

     

定理:是定义在非负整数集合上的两个函数,并且满足条件,那么我们得到结论


在上面的公式中有一个函数,它的定义如下:

    (1)若,那么

    (2)若均为互异素数,那么

    (3)其它情况下


对于函数,它有如下的常见性质:

    (1)对任意正整数

                           

       利用该性质,可O(NlogN)的求出1~N的μ值

    (2)对任意正整数

          


#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <map>
#define clr(x, y) memset(x, y, sizeof x)
using namespace std;
typedef long long LL;
const LL mod=258280327;
const int MAXN=1e5+10;
int mu[MAXN];
int a[MAXN];
int cnt[MAXN];
vector <int> fac[MAXN];
LL po[MAXN];
LL jc[MAXN];
LL F1[MAXN],f1[MAXN];
LL F2[MAXN],f2[MAXN];
int n;
void init()
{
    //预处理1~100000的莫比乌斯系数
    for(int i=1;i<MAXN;i++)
    {
        int target= i==1 ? 1:0;
        int delta=target-mu[i];
        mu[i]=delta;
        for(int j=i+i;j<MAXN;j+=i)
            mu[j]+=delta;
    }
    //预处理阶乘和2的幂
    jc[0]=jc[1]=1;
    po[0]=1; po[1]=2;
    for(int i=2;i<MAXN;i++)
    {
        jc[i]=(jc[i-1]*i)%mod;
        po[i]=(po[i-1]*2)%mod;
    }
    //预处理1~100000的因子
    for(int i=1;i<MAXN;i++)
    {
        fac[i].clear();
    }
    for(int i=1;i<MAXN;i++)
    {
        for(int j=i;j<MAXN;j+=i)
        {
            fac[j].push_back(i);
        }
    }
}
void cal(int maxn,LL &sum1,LL &sum2)
{
    for(int i=1;i<=maxn;i++)
    {
        F1[i]=0;
        LL temp=cnt[i];
        for(int j=1;j<=cnt[i];j++)
        {
            F1[i]=(F1[i]+temp*jc[n-j+1]%mod)%mod;
            temp=temp*(cnt[i]-j)%mod;
        }
        F2[i]=cnt[i]*po[cnt[i]-1]%mod;
    }
    for(int i=1;i<=maxn;i++)
    {
        f1[i]=0; f2[i]=0;
        for(int j=i;j<=maxn;j+=i)
        {
            f1[i]=(f1[i]+mu[j/i]*F1[j])%mod;
            f2[i]=(f2[i]+mu[j/i]*F2[j])%mod;
        }
    }
    sum1=sum2=0;
    for(int i=1;i<=maxn;i++)
    {
        sum1=(sum1+f1[i]*i)%mod;
        sum2=(sum2+f2[i]*i)%mod;
    }
    sum1=(sum1+mod)%mod;
    sum2=(sum2+mod)%mod;
}
int main()
{
    init();
    while(scanf("%d",&n)!=EOF)
    {
        memset(cnt,0,sizeof(cnt));
        int maxn=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            maxn=max(maxn,a[i]);
            for(int j=0;j<fac[a[i]].size();j++)
                cnt[fac[a[i]][j]]++;
        }
        LL sum1,sum2;
        cal(maxn,sum1,sum2);
        if(sum1>sum2)
            printf("Mr. Zstu %I64d\n",sum1);
        else if(sum1<sum2)
            printf("Mr. Hdu %I64d\n",sum2);
        else
            printf("Equal %I64d\n",sum2);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值