zoukankan      html  css  js  c++  java
  • 反演小结

    反演小结

    有一类问题是这样的,已知(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)次幂是等价的

    于是问题得到了圆满解决

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

  • 相关阅读:
    .NetCore 3.1和.NetCore 5.0 中WebApi的请求参数的验证方法
    php 使脚本持续的运行
    Elasticsearch es三种分页方式和对比
    es 大批量写入提高性能的策略
    php 使用多进程批量插入数据
    【转】EM算法MATLAB代码及详细注解
    【转】详解EM算法与混合高斯模型(Gaussian mixture model, GMM)
    【转】高斯混合模型
    【转】二维高斯分布(Two-dimensional Gaussian distribution)的参数分析
    word使用dot模板以spring word 模板为例
  • 原文地址:https://www.cnblogs.com/encodetalker/p/10713450.html
Copyright © 2011-2022 走看看