这个科技是用来 O(k+logn) 求 ∑ni=0[xi]f(x)pi(x)modxk+1 这个多项式的某些项数的线性组合 不过我只见过求第 k 项的,其中 f(x) 微分可导。
要直接说算法本身的话过于枯燥,从例题开始慢慢说吧。
求:
n∑i=0(ni)ik
将 (ni) 看成 [xi](1+x)n,ik 看成 [xkk!]eix,那么原式变成了:
[xkk!]n∑i=0[xi](1+x)neix
相当于
[xkk!](ex+1)n
当然你也可以将 ik 直接看成 [xkk!]eix 然后使用二项式定理得到上式。
这里已经可以保留 (ex+1)modxk+1,通过多项式快速幂 O(klogklogn) 计算答案了,但是我们显然不满足于此。
接下来是重要的一步:
设 F(z)=(z+1)n,F(z+1)=F(z+1)modzk+1。
为什么这里是 z+1 而不是 z 呢?
实际上是因为这里的 z=ex−1,常数项为 0。
为什么常数项一定要为 0?
考虑原式,你算的是 ∑ni=0[xi]f(x)pi(x),答案只截取到第 k 项,那么如果 [x0]p(x)=0,我们就可以只求和到 k。
换而言之,高次项对答案没有贡献。
所以在这个算法中,在这一步中要设的是 F(z+G(0))=F(z+G(0))modxk+1。
因为 F(z+1) 是 F(z+1) 的前 k+1 次项,所以答案 [xkk!]F(ex)=[xkk!]F(ex)。
问题来了,我们如何得到 F(z+1)?
我们对 F(z) 列出一个方程:
(z+1)F′(z)−nF(z)=0
那么对于 F(z+1),呢?
(z+2)F′(z+1)−nF(z+1)=0
好像不对?
是不是多算了什么东西?
考虑到原本不应该有的 [zk](z+2)F′(z+1) 突然出现,所以应该在右边加上这玩意儿。
(z+2)F′(z+1)−nF(z+1)=[zk+1](z+1)F′(z)=(k−n)(nk)zk2n−k
对这个提取系数就可以 O(k) 递推 F(z+1) 了。
提取系数后得到:
F[i]=(k−n)(nk)(ki−1)(−1)k−i+12n−k+(n−i+1)F[i−1]i
不过要注意 F[0] 的值,咱还没有算。。。
注意 F[0] 的定义是 F(z+1) 的 0 次项,也就是:
[z0]k∑i=0[zi]F(z+1)(z−1)i
还记得 F(z+1)=F(z+1)modxk+1 吧?那么 [zi]F(z+1) 就可以使用二项式定理计算了。
k∑i=0([z0](z−1)i)×([zi](z+2)n)
k∑i=0(−1)i×(ni)×2n−i
回到原式:
[xkk!]F(ex)=k∑i=0eix[xi]F(x)
也就是:
k∑i=0ik[xi]F(x)
线性筛 idk 就可以做到 O(k+logn) 啦。
#include<cstdio>
typedef unsigned uint;
const uint M=5005,mod=1e9+7;
uint n,k,top,pri[M],pos[M],idk[M];uint F[M],p2[M],Ck[M],Cn[M],inv[M];
inline uint Add(const uint&a,const uint&b){
return a+b>=mod?a+b-mod:a+b;
}
inline uint Del(const uint&a,const uint&b){
return b>a?a-b+mod:a-b;
}
inline uint pow(uint a,uint b){
uint ans=1;
for(;b;b>>=1,a=1ull*a*a%mod)if(b&1)ans=1ull*ans*a%mod;
return ans;
}
inline void sieve(const uint&M){
register uint i,j,x;idk[1]=1;
for(i=2;i<=M;++i){
if(!pos[i])idk[pri[pos[i]=++top]=i]=pow(i,k);
for(j=1;j<=top&&(x=i*pri[j])<=M;++j){
idk[x]=1ull*idk[i]*idk[pri[j]]%mod;
if((pos[x]=j)==pos[i])break;
}
}
}
signed main(){
register uint i,x,ans=0;inv[0]=1;inv[1]=1;
scanf("%u%u",&n,&k);Cn[0]=Ck[0]=1;Ck[1]=k;Cn[1]=n;sieve(k);
for(i=2;i<=k;++i){
inv[i]=1ull*(mod-mod/i)*inv[mod%i]%mod;
Cn[i]=1ull*Cn[i-1]*(n-i+1)%mod*inv[i]%mod;
Ck[i]=1ull*Ck[i-1]*(k-i+1)%mod*inv[i]%mod;
}
if(n<=k){
for(i=1;i<=n;++i)ans=Add(ans,1ull*Cn[i]*idk[i]%mod);
return!printf("%u",ans);
}
x=pow(2,n-k);
for(i=k;i<=k;--i){
if(i&1)F[0]=Del(F[0],1ull*Cn[i]*x%mod);
else F[0]=Add(F[0],1ull*Cn[i]*x%mod);x=Add(x,x);
}
x=1ull*(n-k)*Cn[k]%mod*pow(2,n-k)%mod;
for(i=1;i<=k;++i){
if(k-i&1)F[i]=Del(1ull*(n-i+1)*F[i-1]%mod,1ull*x*Ck[i-1]%mod);
else F[i]=Add(1ull*(n-i+1)*F[i-1]%mod,1ull*x*Ck[i-1]%mod);
F[i]=1ull*F[i]*inv[i]%mod;
}
for(i=1;i<=k;++i)ans=Add(ans,1ull*idk[i]*F[i]%mod);
printf("%u",ans);
}
求:
n∑i=0fibi×ik
和上面一样:
[xkk!]n∑i=0fibi×eix
设 F(x)=∑ni=0fibi×xi=11−x−x2modxn+1。
[xkk!]F(ex)
如果不截取的话有:
F(z)=zF(z)+z2F(z)+1
截取后,右边多了 n+1 和 n+2 次项的贡献,应该减去。也就是:
F(z)=zF(z)+z2F(z)+1−(fibn−1+fibn)zn+1−fibnzn+2
F(z)=1−(fibn−1+fibn)zn+1−fibnzn+21−z−z2
因为需要求 F(ex),所以还是设 F(z+1)=F(z+1)modzk+1。
再设一个 G(z)=1−(fibn−1+fibn)zn+1−fibnzn+2
F(z+1)=−G(z+1)1+3z+z2
F(z+1)=−(G(z+1)+3zF(z+1)+z2F(z+1))
原本在这里可以提取系数直接递推的,但是我们发现我们不会求 G(z+1)。。。
不过我们发现我们又回到了求一个 GF 的问题,可以继续使用这个科技。
再设一个 G(z+1)=G(z+1)modzk+1。
考虑一个:
Hn(z+1)=(z+1)nmodxk+1
有:
G(z+1)=1−(fibn−1+fibn)Hn+1(z+1)−fibnHn+2(z+1)
所以只需要将 Hn+1(z+1) 和 Hn+2(z+1) 递推出来就可以递推 G(z+1)。
利用求导有:
(z+1)((z+1)n)′=n((z+1)n)
(z+1)H′n(z+1)=nHn(z+1)+(n−k)(nk)zk
于是可以递推 Hn+1(z+1),Hn+2(z+1) 和 G(z+1)。
回代:
[zi]F(z+1)=[i=k+1](3([zk]F(z+1))+([zk−1]F(z+1)))+[i=k+2]([zk]F(z+1))−([zi]G(z+1)+3[zi−1]F(z+1)+[zi−2]F(z+1))
感觉好麻烦啊。。。
为了方便,设 a=3([zk]F(z+1))+([zk−1]F(z+1)),b=[zk]F(z+1)。
将上式化简:
F(z+1)=azk+1+bzk+2−G(z+1)1+3z+z2
也就是:
F(z)=a(z−1)k+1+b(z−1)k+2−G(z)1−z−z2
这下可以递推 F(z) 啦。
回代的结果和例题1一样,都是 ∑ki=0ik[xi]F(x)。
总结
计算 F(G(x)) 时,设:
F(z+G(0))=F(z+G(0))modzk+1
然后通过求导列出关于 F(z+1) 的方程,将 F 替换为 F 后加上/减去被少算/多算的项,然后提取系数继续递推。
只不过列出方程后求解的方法多种多样罢了。
参考资料:
Elegia 「实验性讲稿」载谭 Binomial Sums 详解
Elegia 载谭 Binomial Sum:多项式复合、插值与泰勒展开
GuidingStar CF932E题解
GuidingStar 载谭 Binomial Sum 小练习
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】