hdu 6363 bookshelf (容斥+欧拉降幂)

http://acm.hdu.edu.cn/showproblem.php?pid=6363

题意:

有 N 本一摸一样的书,有一个共有K 层的书架,现在要把书都放到书架上。

放完后假设第 i层书架有 Bi本书,则该层书架的稳固值为2^{Bi}-1 。

定义整个书架的美观值为所有层书架的稳固值的 GCD 。

问现在随机放这些书,整个书架的美观值的期望值是多少。

思路:

Part 1

一个放 x 本书的层美观函数为 b(x)=2^{Fx}-1

整个书架的美观值为各层美观值的最大公约数,考察任意两项:

  1. gcd(b(x),b(y)) =gcd(2^{F_x}-1,2^{F_y}-1) =2^{gcd(F_x,F_y)}-1 =2^{F_{gcd(x,y)}}-1

所以,对于 n=x_1+x_2+..+x_k(x_i>=0) 的任一有序拆分方法来说:

  1. 设他们的 gcd 为 g = gcd(x_1,x_2,x_3...x_k)
  2. 书架的美观值即 = 2^{F_g}-1
  3. 所以我们枚举 g 然后只要能计算公共GCD是 g 的方案数有多少种即可。

Part 2

那么首先这个 gg 一定是 nn 的约数,所以我们直接对 nn 的约数容斥就好了。

我们可以计算约数至少为 gg 的方案数:

  1. 每一层都是 g 的倍数,所以我们只要分配系数即可。
  2. 系数的和为\frac{n}{g},共 k 个系数,每个非负,方案数是个经典组合数 C(\frac{n}{g} + k - 1, k - 1)

然后容斥下把是 g的倍数但不是 g 的那些方案数扣掉。

就得到了严格是 g的方案数,所以从大的约数往小的约数做。

Part 3

最后就只剩下一个细节:计算 2^{F_g}​​​​ 这个东西。

由于F_g非常的爆炸,所以要采用欧拉降幂。

因为他的数满足斐波那契数列,2^{F_g}可以直接预处理出来。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=1e9+7;
const int maxn=2e6+10;
ll inv[maxn],fac[maxn],a[maxn],f[maxn];
ll poww(ll a,ll b)
{
    ll ans=1;
    a%=mod;
    while(b)
    {
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
void init()
{
    f[0]=1;f[1]=2;
    fac[0]=fac[1]=1;
    inv[1]=1;
    for(int i=2;i<maxn;i++)
    {
        fac[i] = fac[i-1] * (ll)i % mod;
        inv[i] = poww(fac[i], mod-2);
        f[i] = (f[i-1] * f[i-2]) % mod;
    }
}
ll C(ll n,ll m)
{
    if(n<m)return 0;
    if(m==0||n==m)return 1LL;
    if(n-1==m||m==1)return n;
    return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        ll n,k;
        scanf("%lld%lld",&n,&k);
        memset(a,0,sizeof(a));
        ll sum=C(n+k-1,k-1),ans=0;
        for(ll i=n;i>=1;--i)
        {
            if(n%i==0)
            {
                ll m=n/i;
                a[i]=C(m+k-1,k-1)%mod;
                for(ll j=2*i;j<=n;j+=i)
                {
                    a[i]=(a[i]-a[j]+mod)%mod;
                }
                ans=ans+a[i]*(f[i]-1+mod)%mod;
                ans%=mod;
            }
        }
        ans=ans*poww(sum,mod-2)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/imzxww/article/details/81545624
今日推荐