炫酷反演魔术 魔术揭秘

来源:互联网 发布:程序员编程动漫 编辑:程序博客网 时间:2024/04/30 12:34

刚才的标题是唬人的…

请允许我先丢个链接,然后开始扯淡.

开场

vfk老师在这份课件中深入浅出地讲解了各种OI中常用的反演,本蒟蒻表示并没有完全看懂(尤其是对于矩阵的一部分知识不是非常熟悉),而且由于网上关于反演的资料除了莫比乌斯反演之外非常少,所以本蒟蒻在这里只是谈谈个人对反演的理解,如果出现了错误还请各路神犇不吝赐教.

还有就是课件中的推导已经写得非常漂亮了,我也没必要在这里再自己敲一遍(所以写这篇博客的目的何在?),有些地方还是直接”抛结论跑”好了.

揭秘

反演的思路是用未知量表示已知量,然后反过来推出未知量的表达式.下面我们默认表示已知量,表示未知量.

课件中介绍了二项式反演,莫比乌斯反演,子集反演等等,这些反演都与容斥原理有着密不可分的关系,就是说都可以用容斥来偏”意识流”地理解.但是不管怎么说,用反演来推导显得比较有说(zhuang)服(bi)力(fan).

推导反演的过程,就是”找到了一个if语句,说了一句废话,代进去搞搞居然就凑出来了个”.当然这其中需要很多技巧,可以通过看课件中的推导来体会.

二项式反演

推导的if语句是.
说的废话是.

莫比乌斯反演

一个方向:

另一个方向:

说实话我并不知道反方向的是怎么推的,但是结论非常好记.

if语句:.
废话:.

子集反演

一个方向:

另一个方向:

还是只会推正方向的…

if语句:.
废话:.

话说从这里开始变量名就有点奇怪了呢...

这个推导和二项式反演的推导非常相似.

实际运用时,这个东西可以用高维前缀和的写法来做,具体看下面的”卷积”部分.

离散傅里叶变换

这个东西实质上也是反演…但是我并不会用反演推…

给的if语句是.
然而我并不知道该说什么废话…

不过课件里那个”又一道经典题”是可以用这个if语句推的.

or卷积

给定,求:

关于这个名称应该没有正规的定义吧…似乎这个和下面的那个卷积都应该是”子集卷积”?还是看个人理解吧,我比较偏向于专门把下面的那个卷积称为”子集卷积”.

不管怎样,这个卷积是子集反演的一个经典应用.

,类似地定义,可以推导出,求出后用子集反演求出.

具体实现时,直接略无脑地用高维前缀和正着倒着搞就可以实现子集反演了,复杂度是.

另外,卷积可以直接先把集合取补集转化成卷积来做.

Code

void or_conv(){    rep(i,0,n)rep(j,1,1<<n)if(j&1<<i){        A[j]+=A[j^1<<i];        B[j]+=B[j^1<<i];    }    rep(i,0,1<<n)C[i]=A[i]*B[i];    rep(i,0,n)rep(j,1,1<<n)if(j&1<<i)        C[j]-=C[j^1<<i];}

子集卷积

给定,求:

[Math Processing Error]

这个…似乎课件上的推导有些问题(当然很有可能是我没看懂),这里良心地写一下我的推法.

如果直接用交集和并集的限制是推不出来的,我们必须再加一个维度.

[Math Processing Error],
还有[Math Processing Error].

那么:

然后再用子集反演,可以推出:

最终答案就是.

这样复杂度就可以做到了.

但是直接暴力做,复杂度是,凭借极小的常数在时仍可以碾压标程….

Code

const int N=17,M=1<<N;int n,bitcnt[M],A[M],B[M],C[M],sum_A[N+1][M],sum_B[N+1][M],sum_C[N+1][M];inline void init(){    rep(i,1,M)bitcnt[i]=bitcnt[i>>1]+(i&1);}void conv(){    rep(i,0,n+1)rep(j,0,1<<n){        if(i==bitcnt[j]){            sum_A[i][j]=A[j];            sum_B[i][j]=B[j];        }        else sum_A[i][j]=sum_B[i][j]=0;    }    rep(i,0,n)rep(j,1,1<<n)if(j&1<<i)rep(k,0,bitcnt[j]){        mod_add(sum_A[k][j],sum_A[k][j^1<<i]);        mod_add(sum_B[k][j],sum_B[k][j^1<<i]);    }    rep(i,0,n+1)rep(j,0,1<<n){        sum_C[i][j]=0;        rep(k,0,i+1)sum_C[i][j]=(sum_C[i][j]+1ll*sum_A[k][j]*sum_B[i-k][j])%mod;    }    rep(i,0,n)rep(j,1,1<<n)if(j&1<<i)rep(k,0,n+1)        mod_minus(sum_C[k][j],sum_C[k][j^1<<i]);    rep(i,0,1<<n)C[i]=sum_C[bitcnt[i]][i];}

收场

妈呀最怕这种时候要我总结点什么了!

大概我对反演的理解也就是这些了…
我觉得学习反演,不应该只记个结论,中间的推导过程也是很有价值的.
那就先这样吧.

0 0
原创粉丝点击