我是靠谱客的博主 贪玩机器猫,最近开发中收集的这篇文章主要介绍[真学习笔记] 前夕 - 单位根反演 - 广义容斥,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这次是真的学习笔记了……
真正意义上搞明白广义容斥实在说啥……
真正搞明白了单位根的那个性质……

题目大意:有个大小为n的集合S,求所有选出若干非空且互不相等的子集使得交集大小是k的倍数。要求一个O(nk)的做法。
先来说说二项式反演这件事情:
P ( x ) = ∑ k = 0 x Q ( k ) ( x k ) Q ( x ) = ∑ k = 0 x P ( k ) ( x k ) ( − 1 ) x − k P(x)=sum_{k=0}^x Q(k)binom xk\Q(x)=sum_{k=0}^x P(k)binom xk (-1)^{x-k} P(x)=k=0xQ(k)(kx)Q(x)=k=0xP(k)(kx)(1)xk
这个显然是对的,因为你至少可以把互相带入……
知道了这件事情我们就可以去做一些广义容斥,例如,我们假定我们已经知道了P(k)表示大小至少k的答案,然后记Q(k)表示大小恰好k的答案,我们想通过P直接算出Q,也就是:
Q ( k ) = ∑ i = 0 n P ( i ) f ( i ) Q(k)=sum_{i=0}^nP(i)f(i) Q(k)=i=0nP(i)f(i)
也就是给P加一个系数f。
而左半部分就是下式,考虑一个大小是i的集合会被P(x)f(x)计算C(x, i)f(x)次:
∑ i = 0 n Q ( i ) ∑ j = 0 i ( i j ) f ( j ) sum_{i=0}^nQ(i)sum_{j=0}^ibinom ij f(j) i=0nQ(i)j=0i(ji)f(j)
而我们最后希望这个东西是: ∑ i = 0 n Q ( i ) [ i = = k ] sum_{i=0}^nQ(i)[i==k] i=0nQ(i)[i==k]
也就是 ∑ i = 0 x ( x i ) f ( i ) = [ x = = k ] sum_{i=0}^xbinom xif(i)=[x==k] i=0x(ix)f(i)=[x==k]
我们把右边看做二项式反演的Q,那么有:
f ( x ) = ∑ i = 0 x [ i = = k ] ( − 1 ) x − i ( x i ) f(x)=sum_{i=0}^x[i==k](-1)^{x-i}binom xi f(x)=i=0x[i==k](1)xi(ix)
也就是当x< k时 f ( x ) = 0 f(x)=0 f(x)=0否则 f ( x ) = ( − 1 ) x − k ( x k ) f(x)=(-1)^{x-k}binom{x}{k} f(x)=(1)xk(kx)
这样就是常说的一种特殊形式的容斥。
这个题里面,显然取 P ( x ) = ( n x ) ( 2 2 n − k − 1 ) P(x)=binom nxleft(2^{2^{n-k}}-1right) P(x)=(xn)(22nk1)
然后这里的 [ i = = k ] [i==k] [i==k]要变成 [ k ∣ i ] [k|i] [ki],也就是只对k的倍数那里统计。
然后就是单位根的性质:
[ k ∣ n ] = 1 k ∑ i = 0 k − 1 ( w k n ) i [k|n]=frac 1k sum_{i=0}^{k-1}(w_k^n)^i [kn]=k1i=0k1(wkn)i
单位根的一个应用是直接把 w k i w_k^i wki这玩意带入到多项式里面,这样求和之后就只剩下k的倍数的项的系数的和的k倍了。
另一个就是直接展开,像这个题直接像上一个例子那样那[i==k]那里替换成[k|i]然后替换成上面单位根的式子然后推导一番即可。注意到交换求和顺序后(也就是先对不同的单位根求和再加起来)后面那一坨可以用二项式定理缩起来。
代码里面是假定k=4的题。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define N 10000010
#define lint long long
#define p 998244353
using namespace std;
inline int mol(lint x) { return x%=p,(x<0?x+p:x); }
int w[6],v[6],f[N],fi[N],a[N];
inline int fast_pow(int x,int k,int ans=1)
{	for(;k;k>>=1,x=(lint)x*x%p) (k&1)?ans=(lint)ans*x%p:0;return ans;	}
int main()
{
int n;scanf("%d",&n);
w[0]=1,w[1]=fast_pow(3,(p-1)/4),w[2]=(lint)w[1]*w[1]%p,w[3]=(lint)w[1]*w[2]%p;
w[1]=v[1]=mol(w[1]-1),w[2]=v[2]=mol(w[2]-1),w[3]=v[3]=mol(w[3]-1),f[0]=1,a[0]=2;
for(int i=1;i<=n;i++) f[i]=(lint)f[i-1]*i%p,a[i]=(lint)a[i-1]*a[i-1]%p;
fi[n]=fast_pow(f[n],p-2);
for(int i=n-1;i>=0;i--) fi[i]=fi[i+1]*(i+1ll)%p,a[i]=a[i]-1,(a[i]<0?a[i]+=p:0);
lint ans=4ll*a[n]*fi[n]%p;
for(int i=1;i<=n;i++)
ans+=(1ll*w[1]+w[2]+w[3])*fi[i]%p*fi[n-i]%p*a[n-i]%p,
w[1]=(lint)w[1]*v[1]%p,w[2]=(lint)w[2]*v[2]%p,w[3]=(lint)w[3]*v[3]%p;
return !printf("%lldn",(lint)mol(ans)*f[n]%p*fast_pow(4,p-2)%p);
}

最后

以上就是贪玩机器猫为你收集整理的[真学习笔记] 前夕 - 单位根反演 - 广义容斥的全部内容,希望文章能够帮你解决[真学习笔记] 前夕 - 单位根反演 - 广义容斥所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部