考场上的我猜测到了某个题是二项式反演 可惜我不会啊
分治NTT给了60的暴力 然而他们的分治NTT都能跑1e6/dk
回归正题
二项式反演大概就是关于二项式的反演(废话)
一般来说 初始状态都是状压的0/1状态 然后它们有很好的性质 与具体哪一位的状态无关 所以可以直接记录1的个数
又因为反演≈容斥 所以它的两个基本形式就很好看了
$f(n) = \sum_{i=0} ^ n (-1)^i \binom{n}{i} g(i) \Leftrightarrow g(n) = \sum_{i=0} ^n (-1)^i \binom{n}{i} f(i)$
一般这个柿子用不到(吧 因为很少上来就是个容斥形式
$f(n) = \sum _{i=0} ^n \binom{n}{i} g(i) \Leftrightarrow g(n)=\sum_{i=0}^n (-1)^{n-i} \binom{n}{i} f(i)$
一般是这个柿子比较有用qwq
具体证明自行百度吧(~~其实就是我懒~~
例题
[HAOI2016] 染色
其实这个题完全可以不用二项式反演直接容斥 也间接性证明了反演的本质就是容斥
考虑恰好为S有k个的限制 按照套路转化为至少有k个为S
设所求函数为G(x) 容斥函数为F(x)
容斥函数可直接计算$F(x) = \binom{m}{x} (m-i)^{n-ix} \frac{(sx)!}{(x!)^s (n-ix)!}$
然后直接套上上边的容斥就可以了
最后NTT优化即可
以前的代码(反正最后柿子推出来是一样的)
//Love and Freedom. #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #define ll long long #define inf 20021225 #define mdn 1004535809 #define N 300010 #define G 3 using namespace std; int rev[N<<2],inv,jc[N*100],iv[N*100],n,m,s,w[N]; int ksm(int bs,int mi) { int ans=1; while(mi) { if(mi&1) ans=(ll)ans*bs%mdn; bs=(ll)bs*bs%mdn; mi>>=1; } return ans; } int pre(int n) { int lim=1,l=0; while(lim<n) lim<<=1,l++; for(int i=1;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1)); inv=ksm(lim,mdn-2); return lim; } void ntt(int *a,int lim,int opt) { for(int i=1;i<lim;i++) if(i<rev[i]) swap(a[i],a[rev[i]]); for(int k=2,mid=1;k<=lim;k<<=1,mid<<=1) { int Wn=ksm(G,(mdn-1)/k); if(opt) Wn=ksm(Wn,mdn-2); for(int w=1,i=0;i<lim;w=1,i+=k) for(int j=0;j<mid;j++,w=(ll)w*Wn%mdn) { int x=a[i+j],y=(ll)w*a[i+mid+j]%mdn; a[i+j]=(x+y)%mdn; a[i+mid+j]=(x-y+mdn)%mdn; } } if(opt) for(int i=0;i<lim;i++) a[i]=(ll)a[i]*inv%mdn; } int h[N],f[N],ans[N]; int top; int main() { scanf("%d%d%d",&n,&m,&s); for(int i=0;i<=m;i++) scanf("%d",&w[i]); top=min(n/s,m); jc[0]=iv[0]=1; int tt=max(n,m); for(int i=1;i<=tt;i++) jc[i]=(ll)jc[i-1]*i%mdn; iv[tt]=ksm(jc[tt],mdn-2); int qwq=1; for(int i=tt;i;i--) iv[i-1]=(ll)iv[i]*i%mdn; for(int i=0;i<=top;i++) f[i]=(ll)jc[m]*iv[m-i]%mdn*jc[n]%mdn*iv[n-i*s]%mdn*qwq%mdn*ksm(m-i,n-i*s)%mdn,qwq=(ll)qwq*iv[s]%mdn; for(int i=0;i<=top;i++) h[i]=(ll)(mdn+((i&1)?-1:1)*iv[i])%mdn; for(int i=0;i<=(top>>1);i++) swap(h[i],h[top-i]); int lim=pre((top+1)<<1);// printf("%d\n",lim); ntt(h,lim,0); ntt(f,lim,0); for(int i=0;i<lim;i++) ans[i]=(ll)h[i]*f[i]%mdn; ntt(ans,lim,1); int fin=0; for(int i=0;i<=top;i++) fin=(ll)(fin+(ll)ans[top+i]*w[i]%mdn*iv[i]%mdn)%mdn; printf("%d\n",fin); return 0; }