组合数前缀和 & 题解:P5388 [Cnoi2019] 最终幻想
题解怎么都在分段打表啊,这里给出一种更加友好的求解方式(指对电脑友好,不需要打表)。
首先,我们要明确这道题要求的是
这里给出一种时间复杂度为
思路与快速阶乘算法一致,同样先分块,我们发现
注意上面的乘积顺序运算是从右向左的(即每次把矩阵放到左边和之前的结果相乘)。
但是这个矩阵中带有分数,并不好计算,所以先通分,再把分母提出来,即
现在只需要维护
手动计算一下矩阵乘法可以得到
直接每轮跑
最后答案要除以
参考实现:
int sumcomb(int n,int m,const int mod){
m=min(n,m)+1;
// 注意这里,上面式子中乘积下标是从 0 开始的,但是这里计算从 1 开始,所以加上 1 再计算
MPoly f(2),g(2),h(2);
f.setmod(mod),g.setmod(f.fm),h.setmod(f.fm);
int B=1<<int(ceil(0.5*log2(m))),r=qpow(B,mod-2,f.fm),siz=m/B;
f.a[0]=n,f.a[1]=n-B;
g.a[0]=1,g.a[1]=B+1;
h.a[0]=1,h.a[1]=B+1; // 初始值
for (int l=2;l<=B;l<<=1){
f.extend(pointValueTranslation(f,(l>>1)+1));
g.extend(pointValueTranslation(g,(l>>1)+1));
h.extend(pointValueTranslation(h,(l>>1)+1));
MPoly u=pointValueTranslation(f,r*ll(l>>1)%f.fm);
MPoly v=pointValueTranslation(g,r*ll(l>>1)%f.fm);
MPoly w=pointValueTranslation(h,r*ll(l>>1)%f.fm);
g.mulcoefficient(w);
g+=v.mulcoefficient(f);
f.mulcoefficient(u);
h.mulcoefficient(w);
f.resize(l+1),g.resize(l+1),h.resize(l+1);
}
ll res1=1,res2=0,res3=1;
for (int i=0;i<siz;i++){
res2=(res1*g.a[i]+res2*h.a[i])%f.fm;
res1=res1*f.a[i]%f.fm;
res3=res3*h.a[i]%f.fm;
}
for (int i=siz*B+1;i<=m;i++){
res2=(res1+res2)*i%f.fm;
res1=res1*(n-i+1)%f.fm;
res3=res3*i%f.fm;
}
// 计算零散部分
return qpow(res3,f.mod-2,f.fm,res2);
}
时间复杂度为
前面的多项式部分可以看这里。
更多题目:loj6386,U562050(我自己的题,需要任意模数)。