版权声明:欢迎转载 https://blog.csdn.net/animalcoder/article/details/82504400
32.hdu6363 因子容斥+组合模型+数论
题意:有N本一摸一样的书,有一个共有K层的书架,现在要把书都放到书架上。
放完后假设第 i层书架有 Bi本书,则该层书架的稳固值为 (2^Bi)-1
定义整个书架的美观值为所有层书架的稳固值的GCD
问现在随机放这些书,整个书架的美观值的期望值是多少。
思路:直接搬得官方题解,很清晰了
所以我们只用考虑GCD(x1,x2,x3….)的贡献
因子容斥,从大的往小容斥的套路。至少因子是g->恰好为g
那个经典组合数是模型
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod=1e9+7;
const int _=1000005;
int fb[_];//斐波那契
int jc[2*_];//阶乘
int jv[2*_];//阶乘的逆元
int qmod(int a,int b)
{
int res=1;while(b){
if(b&1)res=1ll*res*a%mod;
a=1ll*a*a%mod;b=b>>1;
}
return res;
}
void init()
{
fb[0]=0;fb[1]=1;jc[0]=1;jv[0]=1;
for(int i=2;i<_-2;i++)fb[i]=1ll*(fb[i-1]+fb[i-2])%(mod-1);//扩展欧拉 %(mod-1)
for(int i=1;i<2*_;i++)jc[i]=1ll*jc[i-1]*i%mod;
jv[2*_-1]=qmod(jc[2*_-1],mod-2);
for(int i=2*_-2;i>=1;i--)jv[i]=1ll*jv[i+1]*(i+1)%mod;
}
vector<int>d;int ans[_];
void get(int n)//得N的因子
{
for(int i=1;i<=n/i;i++)
if(n%i==0)
{
if(n!=i*i)d.push_back(i),d.push_back(n/i);
else d.push_back(i);
}
sort(d.begin(),d.end());
}
int getC(int a,int b){
return 1ll*jc[a]*jv[b]%mod*jv[a-b]%mod;
}
int main()
{
init();
//printf("%d\n",getC(7,3));
int T;scanf("%d",&T);
while(T--)
{
int n,k;scanf("%d %d",&n,&k);int res=0;
d.clear();get(n);
for(int i=0;i<d.size();i++)ans[i]=getC(n/d[i]+k-1,k-1)%mod;
//枚举gcd为d[i],剩下n/d[i]球个分到k个不同的盒子里,可以空盒
//至此,ans[i]:gcd至少为i的方案数
//举个例子N=8,ans[2]=f[2]+f[4]+f[8];ans[4]=f[4]+f[8];ans[8]=f[8];
for(int i=d.size()-1;i>=0;i--)
for(int j=i+1;j<d.size();j++)
if(d[j]%d[i]==0)ans[i]=1ll*(ans[i]-ans[j]+mod)%mod;
//因子容斥 ,从大到小搞,算是常见套路!
//for(int i=0;i<d.size();i++)printf("%d %d %d\n",d[i],ans[i],fb[d[i]]);
//至此,ans[i]:gcd为i的方案数(从至少到恰有)
for(int i=0;i<d.size();i++)
(res+=1ll*ans[i]*(qmod(2,fb[d[i]])-1)%mod )%=mod;//对每个gcd算贡献
res=1ll*res*qmod(getC(n+k-1,k-1),mod-2)%mod;//除以总情况,便是期望
printf("%d\n",(res+mod)%mod);
}
}