拉格朗日反演可以互推一对多项式复合逆,拉反通常起到将函数自、因变量交换,简化柿子的作用。
定义
-
复合逆:
则称和互为复合逆,记作 -
引理:
证明
当,有,是整式显然无项;当,有 上下的常数项均为所以值为。
- 拉格朗日反演:若,且满足常数项为,一次项可逆,则
通常使用最右边形式,因为要常数项可逆。
证明
两侧求导得:
同除得:
根据引理1:
-
推论(常用):
-
扩展拉格朗日反演:给定,则
大概意义是复合逆的复合函数。
证明
设
求导可得:
同除得:
根据引理1:
当 时,分母为 就需要用到下面的另类方法
- 另类拉格朗日反演:
证明
展开可得:
两侧同除:
根据引理1:
- 扩展另类拉格朗日反演:
应用
【BZOJ3684】大朋友和多叉树
- 题意:
给你和,在给你个数的集合。问你叶子数为且每个度数大于的点的儿子个数在中的方案数。 - 思路:
为叶子数为的方案数。
发现项比项复杂。考虑拉反(代入)。
由拉反得:
因此贺一下多项式幂函数+多项式求逆的板子就可以了(板子太烂,心累ing~)。 - code:
贺的
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
const int N=1e6+5;
const ll mod=950009857;
ll inv[N],inv7,gen[2][N];
ll ksm(ll a,ll b) {ll mul=1;for(;b;b>>=1,a=a*a%mod)if(b&1)mul=mul*a%mod;return mul;}
int rev[N],L,up;
void NTT(ll *a,int op) {
for(int i=0;i<up;i++) {
if(rev[i]>i)swap(a[i],a[rev[i]]);
}
for(int mid=1;mid<up;mid<<=1) {
int len=mid<<1;ll w1=gen[op][len];
for(int l=0;l<up;l+=len) {
ll W=1;
for(int i=0;i<mid;i++,W=W*w1%mod) {
int p=l+i,q=p+mid;
ll x=a[p],y=W*a[q];
a[p]=(x+y)%mod;a[q]=(x-y)%mod;
}
}
}
}
void gt_up(int len) {
up=1,L=0;
while(up<len) {up<<=1,L++;}
for(int i=1;i<up;i++) {rev[i]=(rev[i>>1]>>1)|((i&1)<<(L-1));}
}
ll a[N];
void poly_inv(int dep,ll *f,ll *b) { // b = f mod(x^dep)
if(dep==1) {b[0]=ksm(f[0],mod-2);return;}
poly_inv((dep+1)>>1,f,b);
gt_up(dep<<1);
for(int i=0;i<dep;i++)a[i]=f[i];for(int i=dep;i<up;i++)b[i]=a[i]=0;
NTT(b,0);NTT(a,0);
for(int i=0;i<up;i++) {a[i]=(2-a[i]*b[i]%mod)*b[i]%mod;}
NTT(a,1);
ll i_up=ksm(up,mod-2);
for(int i=0;i<dep;i++) {b[i]=a[i]*i_up%mod;} for(int i=dep;i<up;i++)b[i]=0;
}
void poly_dao(int n,ll *f,ll *g) {
for(int i=1;i<n;i++) g[i-1]=f[i]*i%mod;g[n-1]=0;
}
void poly_jf(int n,ll *f,ll *g) {
for(int i=1;i<n;i++) g[i]=f[i-1]*inv[i]%mod;g[0]=0;
}
ll d[N],g[N];
void poly_ln(int n,ll *f) {
poly_dao(n,f,d);
for(int i=0;i<n;i++)g[i]=0;poly_inv(n,f,g); //****b must be 1,0....0 at first
gt_up(n<<1);
for(int i=n;i<up;i++)g[i]=d[i]=0;
NTT(d,0),NTT(g,0);
for(int i=0;i<up;i++)g[i]=d[i]*g[i]%mod;
NTT(g,1);
for(int i=0,i_up=ksm(up,mod-2);i<up;i++)g[i]=(i<n)?(g[i]*i_up%mod):0;
poly_jf(n,g,f);
}
ll c[N];
void poly_exp(int dep,ll *f,ll *b) {
if(dep==1) {b[0]=1;return;}
poly_exp((dep+1)>>1,f,b);
for(int i=0;i<dep;i++)c[i]=b[i];
poly_ln(dep,c);
gt_up(dep<<1);
for(int i=0;i<dep;i++)c[i]=(f[i]-c[i])%mod;for(int i=dep;i<up;i++)b[i]=c[i]=0;
c[0]++;
NTT(b,0);NTT(c,0);
for(int i=0;i<up;i++)b[i]=c[i]*b[i]%mod;
NTT(b,1);
for(int i=0,i_up=ksm(up,mod-2);i<up;i++) {
b[i]=(i<dep)?(b[i]*i_up%mod):0;
}
}
void poly_pow(int n,ll *f,ll *g,ll k) {
poly_ln(n,f);
for(int i=0;i<n;i++) {f[i]=f[i]*k%mod;}
poly_exp(n,f,g);
}
void gt_gen(int len) {
inv7=ksm(7,mod-2);
gt_up(len);
up=min(up,1<<21);
gen[0][up]=ksm(7,(mod-1)/up);gen[1][up]=ksm(inv7,(mod-1)/up);
for(int i=up;i;i>>=1) {
gen[0][i>>1]=gen[0][i]*gen[0][i]%mod;
gen[1][i>>1]=gen[1][i]*gen[1][i]%mod;
}
inv[1]=1;for(int i=2;i<up;i++)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
}
ll f[N],oo[N],ans[N];
int main() {
int s,m;
scanf("%d%d",&s,&m);
for(int i=1;i<=m;i++) {int k;scanf("%d",&k);f[k-1]=-1;}
f[0]=1;
gt_gen(s<<1);
poly_inv(s,f,oo);
poly_pow(s,oo,ans,s);
printf("%lld\n",(ans[s-1]*ksm(s,mod-2)%mod+mod)%mod);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
2021-07-11 【JSOI2016】最佳团体