2018 Multi-University Training Contest 6 1002 bookshelf(hdu 6363)(容斥 | 莫比乌斯反演)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yz467796454/article/details/81673051

题目链接:hdu 6363 bookshelf

Sample Input
1
6 8
 

Sample Output
797202805

题意:n本书放到k层书架上,放完后,若某一层有x本书,则这一层的稳固值f(x),f是斐波那契数列,美观值为2^{f_{(x)}}-1,整个书架的美观值为所有层的美观值的gcd,求书架美观值的期望值。

思路:太难了!直接贴题解

这个gcd的性质是数论中有的,不会证明,凑合看吧

莫比乌斯反演学习了一下,实在不会,可以看这篇博客:bookshelf HDU - 6363(数论结论+莫比乌斯反演)

关于莫比乌斯反演可以看这个博客:莫比乌斯反演详解

我的代码是用了下面的方法,可以参照这个博客的解释:HDU-6363:bookshelf(数论+容斥)

 至于欧拉降幂我没有用到,欧拉降幂是说

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<vector>
#define maxn 2000006
using namespace std;
typedef long long ll;
const ll mod=1e9+7;

ll b[maxn],inv[maxn],invf[maxn];
ll f[maxn];
int num[maxn];
ll sum[maxn];
ll mo(ll a,ll pp){
    if(a>=0&&a<pp)return a;
    a%=pp;
    if(a<0)a+=pp;
    return a;
}
ll mo(ll a){
    if(a>=0&&a<mod)return a;
    a%=mod;
    if(a<0)a+=mod;
    return a;
}
ll powmod(ll a,ll b,ll pp){
    ll ans=1;
    for(;b;b>>=1,a=mo(a*a,pp)){
        if(b&1)ans=mo(ans*a,pp);
    }
    return ans;
}
ll powmod(ll a,ll b){
    ll ans=1;
    for(;b;b>>=1,a=mo(a*a)){
        if(b&1)ans=mo(ans*a);
    }
    return ans;
}

ll C(int n,int m){
	if(n<m)return 0;
	if(m==n||m==0)return 1;
	return b[n]*invf[n-m]%mod*invf[m]%mod;
}
void init(){
    b[0]=1;
    for(int i=1;i<maxn;i++)b[i]=b[i-1]*i%mod;
    //for(int i=0;i<maxn;i++)inv[i]=powmod(b[i],mod-2,mod);这样求会TLE 
    inv[1]=1;//逆元
    for(int i=2;i<maxn;i++)inv[i]=((mod-mod/i)*inv[mod%i])%mod;
	invf[0]=1;//累乘逆元
    for(int i=1;i<maxn;i++)invf[i]=(invf[i-1]*inv[i])%mod;
    f[0]=0;f[1]=1;f[2]=1;
    for(int i=3;i<maxn;i++)f[i]=(f[i-1]+f[i-2])%(mod-1);
     
}
ll inv1(ll b){
	return powmod(b,mod-2,mod);
}
ll inv2(ll a){
	if(a==1)return 1;
	return inv2(mod%a)*inv2(mod-mod/a)%mod;
}

int main(){
	int t;
	scanf("%d",&t);
	init();
	while(t--){
		int n,k;
		scanf("%d%d",&n,&k);
		int cnt=0;
		for(int i=1;i<=n;i++){
			if(n%i==0)num[cnt++]=i;//num存n的因子 
		}
		for(int i=0;i<cnt;i++){// 系数和为n/g的方案数 
			sum[i]=C(n/num[i]+k-1,k-1);
		}
		for(int i=cnt-2;i>=0;i--){//把是g的倍数但不是g的方案数去掉 
			for(int j=i+1;j<cnt;j++){
				if(num[j]%num[i]==0){
					sum[i]=(sum[i]-sum[j]+mod)%mod;
				}
			}
		}
		ll ans=0;
		for(int i=0;i<cnt;i++){
			ans=(ans+(powmod(2,f[num[i]])-1+mod)%mod*sum[i]%mod)%mod;
		}
		printf("%lld\n",ans*inv1(C(n+k-1,k-1))%mod);
	}
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/yz467796454/article/details/81673051
今日推荐