反演小结

反演小结

有一类问题是这样的,已知(f(n)=sum_{某些条件}g(i)),求(g(n))

一般我们会根据这个式子求出一个新的式子:(g(n)=sum_{另一些条件}f(i))

我们将满足上面的形式的式子称为反演,反演是一类经典的问题,本文将对与反演有关的问题进行简单的介绍

二项式反演

简介

对于函数(f(n))(g(n)),如果满足

[f(n)=sum_{i=0}^ndbinom{n}{i}g(i) ]

那么

[g(n)=sum_{i=0}^n (-1)^{n-i}dbinom{n}{i}f(i) ]

证明的话考虑代入

[egin{aligned} f(n)=&sum_{i=0}^n dbinom{n}{i}sum_{j=0}^i(-1)^{i-j}dbinom{i}{j}f(j) \ =&sum_{j=0}^nf(j)sum_{i=j}^n(-1)^{i-j}dbinom{i}{j}dbinom{n}{i} end{aligned} ]

注意到有(dbinom{n}{i} dbinom{i}{j}=frac{n!}{i!(n-i)!}*frac{i!}{j!(i-j)!}=frac{n!}{(n-i)!}*frac{1}{j!(i-j)!}=frac{n!}{j!(n-j)!}*frac{(n-j)!}{(n-i)!*(i-j)!}=dbinom{n}{j}dbinom{n-j}{n-i}=dbinom{n}{j}dbinom{n-j}{i-j})

于是就有

[egin{aligned} f(n)=&sum_{j=0}^nf(j)sum_{i=j}^n(-1)^{i-j}dbinom{n}{j}dbinom{n-j}{n-i}\ =&sum_{j=0}^nf(j)dbinom{n}{j}sum_{i=j}^n(-1)^{n-i}dbinom{n-j}{n-i}\ =&sum_{j=0}^nf(j)dbinom{n}{j}(1-1)^{n-j}\ =&sum_{j=0}^nf(j)[n=j]\ =&f(n) end{aligned} ]

类似于莫比乌斯反演(下面会提到),二项式反演也有它的另一种形式,有题目会用到

即已知

[f(k)=sum_{i=k}^ndbinom{i}{k}g(i) ]

那么就有

[g(k)=sum_{i=k}^n(-1)^{i-k}dbinom{i}{k}f(i) ]

证明的话同样考虑代入

[egin{aligned} f(k)=&sum_{i=k}^ndbinom{i}{k}sum_{j=i}^n(-1)^{j-i}dbinom{j}{i}f(j)\ =&sum_{j=k}^nf(j)sum_{i=k}^j(-1)^{j-i}dbinom{j}{i}dbinom{i}{k}\ =&sum_{j=k}^nf(j)sum_{i=k}^j(-1)^{j-i}dbinom{j}{k}dbinom{j-k}{j-i}\ =&sum_{j=k}^nf(j)dbinom{j}{k}sum_{i=k}^j(-1)^{j-i}dbinom{j-k}{j-i}\ =&sum_{j=k}^nf(j)dbinom{j}{k}(1-1)^{j-k}\ =&sum_{j=k}^nf(j)dbinom{j}{k}[j=k]\ =&f(k) end{aligned} ]

二项式反演的本质其实就是容斥,关于其从容斥上的证明在此略去

我们来看几道题目

例题

错排问题

题目:给出一个正整数(n),求有多少个长为(n)的排列(p)满足(p_i eq i)

(f_n)为长为(n)的排列的个数,(g_i)表示长为(n)的排列中恰有(i)个错位的排列个数,那么就有

[f_n=sum_{i=0}^ndbinom{n}{i}g_i=n! ]

反演后可以得到

[g_n=sum_{i=0}^n(-1)^{n-i}dbinom{n}{i}i! ]

直接计算即可

染色问题

题目:有(1-n)(n)个格子依次排在一排,用(m)种染料对它们染色,要求相邻两个格子的颜色不同且每种颜色均出现过至少一次,求方案数

(f_n)为使用不超过(n)种颜色染色的方案数。(g_n)表示恰好使用(n)种颜色染色的方案数,答案就是(g_m)

[f_m=sum_{i=0}^mdbinom{m}{i}g_i=m*(m-1)^{m-1} ]

反演之后就有

[g_m=sum_{i=0}^m(-1)^{m-i}dbinom{m}{i}f_i ]

直接计算即可

莫比乌斯反演

丢下自己很久以前写下的博客

注意一下莫比乌斯反演不只可以借助(mu)函数进行求解,还可以直接通过计算因数的方法进行求解,这一般适用于需要求(f_1-f_n)的值的时候,比如说UOJ #62

z在这里附上vfk的题解(已经讲解的十分详细了):http://vfleaking.blog.uoj.ac/blog/62

以及自己的十分丑陋的代码

#include<iostream>
#include<string.h>
#include<string>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<vector>
#include<queue>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
const int maxd=998244353,N=100000;
const double pi=acos(-1.0);
int n,c,d,q;
ll fr[100100],g[100100],b[100100],z[100100];

int read()
{
	int x=0,f=1;char ch=getchar();
	while ((ch<'0') || (ch>'9')) {if (ch=='-') f=-1;ch=getchar();}
	while ((ch>='0') && (ch<='9')) {x=x*10+(ch-'0');ch=getchar();}
	return x*f;
}

ll qpow(ll x,ll y)
{
	ll ans=1,sum=x;
	while (y)
	{
		int tmp=y%2;y/=2;
		if (tmp) ans=(ans*sum)%maxd;
		sum=(sum*sum)%maxd;
	}
	return ans;
}

ll inv(ll x) {return qpow(x,maxd-2);}

void init()
{
	n=read();c=read();d=read();q=read();
	c%=(maxd-1);d%=(maxd-1);
	c-=d;
	c=(c+maxd-1)%(maxd-1);
	int i,j;
	for (i=1;i<=n;i++) fr[i]=qpow(i,c);
	for (i=1;i<=n;i++)
	{
		for (j=i*2;j<=n;j+=i)
		{
			fr[j]=(fr[j]+maxd-fr[i])%maxd;
		}
	}
	//for (i=1;i<=n;i++) cout << fr[i] << " ";cout << endl;
	for (i=1;i<=n;i++) 
	{
		fr[i]=inv(fr[i]);g[i]=inv(qpow(i,d));
	}
	//for (i=1;i<=n;i++) cout << fr[i] << " ";cout << endl;
}

void work()
{
	int i,j;
	for (i=1;i<=n;i++) {b[i]=read();b[i]=(b[i]*g[i])%maxd;}
	for (i=1;i<=n;i++) 
	{
		for (j=i+i;j<=n;j+=i)
		{
			b[j]=(b[j]+maxd-b[i])%maxd;
		}
	}
	for (i=1;i<=n;i++)
	{
		if ((b[i]!=0) && (fr[i]==0)) {printf("-1
");return;}
		b[i]=(b[i]*fr[i])%maxd;
	}
	for (i=n;i>=1;i--) 
	{
		for (j=i+i;j<=n;j+=i)
		{
			b[i]=(b[i]+maxd-b[j])%maxd;
		}
	}
	for (i=1;i<=n;i++) b[i]=(b[i]*g[i])%maxd;
	for (i=1;i<=n;i++) printf("%lld ",b[i]);printf("
");
}

int main()
{
	init();
	while (q--) work();
	return 0;
}

单位根反演

简介

单位根是什么?上网搜一篇(FFT)的博客,里面一定会提到单位根

或者您可以看这篇:https://www.cnblogs.com/zhou2003/p/10212036.html

注意到之后我们会用到单位根的诸多性质,理解单位根一定不要丢下单位圆

那么单位根反演又是什么呢?其实就是下面这个式子:

[[n|k]=frac{1}{n}sum_{i=0}^{n-1} omega_n^{ik} ]

证明的话考虑分类讨论,当(n|k)

[frac{1}{n}sum_{i=0}^{n-1}omega_n^{ik}=frac{1}{n}sum_{i=0}^{n-1}1=1 ]

但是当(n mid k)时,这就是一个简单的等比数列求和求和问题

[frac{1}{n}sum_{i=0}^{n-1}omega_n^{ik}=frac{1}{n}*w_n^0frac{1-w_n^{nk}}{1-w_n^k}=0 ]

利用这个东西我们可以求这样的一个东西:对于序列(a_1,a_1,cdots,a_n),求(sum_{k|i}a_i)

我们考虑序列(a)的生成函数(f(x))

由单位根反演我们知道

[egin{aligned} sum_{k|i}a_i&=frac{1}{k}sum_{i=1}^{n}a_isum_{j=0}^{k-1}w_k^{ij}\ &=frac{1}{k}sum_{j=0}^{k-1}sum_{i=1}^na_iomega_k^{ij}\ &=frac{1}{k}sum_{j=0}^{k-1}f(omega_k^j) end{aligned} ]

将单位根带回原多项式求值就是(FFT)的前半部分(也就是(DFT)),可以在(O(nlogn))的时间内完成

我们将上面这个问题推广一下,给出(n,k,m),求(sum_{i ext%k=m}a_i)

比如说现在(m=1)

我们来看一下原生成函数的形式:(f(x)=a_1x+a_2x^2+cdots+a_nx^n)

将其除以(x)之后得到(f'(x)=a_1+a_2x+a_3x^2+cdots+a_nx^{n-1})

也就是说我们将在(f(x))代入的单位根在(f'(x))中代入,就可以求出(sum_{i ext%k=1})的项了

这其实和将代入的单位根除以自己的(m)次幂是等价的

于是问题得到了圆满解决

(电脑没电了,明天补例题,就是咕咕了)

原文地址:https://www.cnblogs.com/encodetalker/p/10713450.html