题意:
总共有N本书,要放到一个K层的书架上去。假设每一层最后放了本书,f[i]为斐波那契数列(f[0]=0,f[1]=1),定义第i层书架的稳定值为,第i层书架的美观值为,定义分数为。问分数的期望为多少。
思路:
有两个公式,
,其中a,m,n>0。
。
假设每一层放了本书,即,则分数为,由第一个公式,有。由第二个公式,继续化为。
设,则有,相加有,所以g可以由枚举n的因子得到。
对于每个g,问题就转化为了将个系数分配给k层的问题,这是可以有空位的整数拆分问题,方案数为(具体可见https://wenku.baidu.com/view/6f822211dd36a32d7375816b.html)。
然而这样子是有重复的,比如n=15,k=1,那么g有1,3,5,15,如果直接算方案数应该是1+1+1+1,但是事实上都是重复的,实际只有一种情况,所以这里就要用到容斥定理了。
要把某个数倍数的情况都从这个小的数里减去(如果把大的置0是不行的,因为这个数可能是多个数的倍数,那它的其他因数的方案就还是重复的了),而且要从大到小枚举,比如上个例子,如果从小到大的话1就被减了多次,其实是不能这样的,而是得把比它大的都处理完才行。
刚开始一直想不出这里容斥原理要怎么用,后来还是看了代码理解的…举个栗子就清楚一点。
对于的处理,因为非常大,所以可以用欧拉降幂公式或者费马小定理(因为2与MOD互质)。。
要计算期望,分子为分数*该分数的方案数,分母为总方案数。
代码:
typedef long long ll;
ll fac[2000010];
ll inv[2000010];
ll f[1000010];
ll sum[1000010];
long long PowerMod(long long a,long long b,long long c)
{
long long ans=1;
long long k;
k=a;
k=k%c;
while(b>0)
{
if(b&1)
ans=(ans*k)%c;
b>>=1;
k=(k*k)%c;
}
return ans;
}
void init()
{
fac[0]=fac[1]=1;
for(int i=2;i<=MAXN;i++)
fac[i]=fac[i-1]*i%MOD;
inv[MAXN]=PowerMod(fac[MAXN],MOD-2,MOD);
for(int i=MAXN-1;i>=0;i--)
inv[i]=inv[i+1]*(i+1)%MOD;
f[0]=0;f[1]=1;
for(int i=2;i<=1000000;i++)
f[i]=(f[i-1]+f[i-2])%(MOD-1);
}
ll calc(int n,int m)
{
if(n<m)return 0;
return fac[n]*inv[m]%MOD*inv[n-m]%MOD;
}
int main()
{
int t,n,k;
init();
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&k);
vector<int>ft;
for(int i=1;i<=n;i++)//枚举g
if(n%i==0)ft.push_back(i);
int len=ft.size();
for(int i=0;i<len;i++)
sum[i]=calc(n/ft[i]+k-1,k-1);
/*for(int i=0;i<len;i++)
printf("%lld ",sum[i]);
printf("\n");*/
for(int i=len-1;i>=0;i--)
for(int j=i+1;j<len;j++)
if(ft[j]%ft[i]==0)
sum[i]=(sum[i]-sum[j]+MOD)%MOD;
/*for(int i=0;i<len;i++)
printf("%lld ",sum[i]);
printf("\n");*/
ll ans=0;
for(int i=0;i<len;i++)
ans=(ans+(((PowerMod(2ll,f[ft[i]]%(MOD-1),MOD)-1)*sum[i])%MOD))%MOD;
ll tot=calc(n+k-1,k-1);
ans=(ans*PowerMod(tot,MOD-2,MOD))%MOD;
printf("%lld\n",ans);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!