各种多项式和生成函数科技题

\(\LaTeX\) 渲染的速度较慢,请稍等

或许更好的阅读体验

更新记录

upd on 2023.1.3: 新建 md 文档, 添加第一道题。

upd on 2023.1.8: 添加 8 道题

upd on 2023.2.1:中间更新过好几次,忘了多少次了,反正现在有 29 道题

[THUPC2017] 小 L 的计算题

题意:\(\displaystyle f_k=\sum_i^n {a_i}^k \pmod {998244353}\),求 \(f_{1\dots k}\)

题解: 构造生成函数,\(\displaystyle F(x)=\sum _{i\geq 0}^\infty x^i \sum_{j=1}^n {a_j}^i\)

$\displaystyle F(x)=\sum_i ^\infty \sum_j^n (xa_j)^i $

\(\displaystyle F(x)=\sum _{j=1}^n \sum _{i=0 }^\infty (xa_j)^i\)

$\displaystyle F(x)=\sum_{j=0}^n \frac {xa_j}{1-xa_j} $(等比数列求和)

然后到这里就可以分治+NTT 算了,具体就是分别维护分子和分母。

然后可以接着推:

$\displaystyle F(x)=\frac {\sum_{j=1}^n x a_j\prod _{i\not =j}(1-xa_i)}{\prod _{i=1}^n(1-xa_i)} $

其中分母部分求个导就是 \(\displaystyle \sum _{j=1}^n -xa_j \prod _{i\not = j} (1-xa_j)\)

这个部分就是分子的相反数。 所以只需要给分母分治+NTT算出来,分母求个导,再取个负就是分子了。然后还需要多项式求逆就完事了

P5488 差分与前缀和

题面:对于长度为 \(n\) 的数组 \(a\),求出其 \(k\) 阶前缀和或差分数组。

题解:

设序列 \(a\) 的生成函数 \(A(x)=\sum \limits _{i=1} a_i x^i\)

如果是前缀和的话,就是需要将它乘上 \(G(x)=1+x^1+x^2+\dots +x^\infty\)

等比数列求和公式告诉我们:\(G(x)=\frac{1}{1-x}\)

所以 \(k\) 阶前缀和就是乘上 \(G^k(x)\)

如果是差分的话,就是要乘上 \(1-x\)

所以 \(k\) 阶差分就是乘 \(k\) 次。

然后应该一些多项式科技就能解了不过我没写。

首先是前缀和的式子:

根据广义二项式定理: \((1-x)^\alpha=\sum\limits _{i=0}^\infty(-1)^i \dbinom{\alpha }{i}x^i\)

然后开始乱推

\((1-x)^{-k}=\sum\limits_{i=0}^\infty (-1)^i\dbinom{-k}{i}x^i\)

\(=\sum\limits (-1)^i\frac {(-k)(-k-1)(-k-2)\dots (-k-i+1)}{i!}x^i\)

然后全都提出个负号。

\(=\sum\limits_{i=0}^\infty (-1)^{2i}\dbinom{k+i-1}{i}x^i\)

\(=\sum \limits _{i=0}^\infty \dbinom{k+i-1}{i}x^i\)

然后就是直接和 \(A(x)\) 卷起来就行了。

然后咱们看差分。

\((1-x)^k=\sum\limits _{i=0}^\infty (-1)^i\dbinom{k}{i}x^i\)

这个直接和 \(A(x)\) 卷起来就行。

P4451 [国家集训队]整数的lqp拆分

假设 \(g_n\) 表示所有数字和 \(n\) 的权值和,那么就有 \(\sum \limits _{i=0}^{n} g_ifib_{n-i}\)

特别的,令 \(g_0=1\),然后是设 \(G(x)\)\(g\) 的生成函数。

\(\displaystyle G(x)=1+\sum_{i=1}^\infty x^i\sum_{j=0}^i g_{j}fib_{i-j}\)

\(G(x)=\sum\limits _{i=0}^\infty ([i==0]+\sum _{j=0}^{i}g_jfib_{i-j})x^i\)

里面明显有个卷积,设斐波那契数列的生成函数为 \(F(x)\)

\(G(x)=1+F(x)G(x)\)

\(G(x)=\frac {1}{1-F(x)}\)

然后众所周知的斐波那契数列的推导:\(x^2F(x)+xF(x)+x=F(x)\)

\(F(x)=\frac{x}{-x^2-x+1}\)

然后代入 \(G(x)=\frac {1}{1-\frac {x}{-x^2-x+1}}\)

然后经过推导(懒得推了\(G(x)=1-\frac {x}{x^2+2x-1}\)

然后就来解决 \(\frac {-x}{x^2+2x-1}\)

就是设方程 \(x^2+2x-1=0\) 的两根 \(x_1,x_2\)

\(\frac {-x}{x^2+2x-1}=\frac {-x}{(x-x_1)(x-x_2)}\)

\(=\frac {-x}{(x_1-x_2)}\times(\frac {x_1-x_2}{(x-x_1)(x-x_2)})\)

\(=\frac {-x}{x_1-x_2}\times (\frac {1}{x-x_1}-\frac {1}{x-x_2})\)

然后把括号里面的部分的常数项提成 \(1\)

\(=\frac {x}{x_1-x_2}(\frac {1}{x_1}\frac {1}{1-\frac {1}{x_1}x}-\frac {1}{x_2}\frac {1}{1-\frac {1}{x_2}x})\)

里面的两部分为等比数列的求和公式。

所以就能拆成 \(\frac {1}{x_1-x_2}\sum\limits_{i=1} (\frac {x^i}{{x_1}^i}-\frac {x^i}{{x_2}^i})\)

所以第 \(n\) 项系数为 \(\frac {1}{x_1-x_2}(\frac {1}{{x_1}^n}-\frac {1}{{x_2}^n})\)

把数字代入,\(\frac {\sqrt 2}{4}[(1+\sqrt 2)^n-(1-\sqrt 2)^n]\)

通项有了,然后 \(\sqrt 2\) 可以暴力求, \(n\)\(\mod mod-1\)

P4705 玩游戏

假期望,就是求 \(\frac {1}{nm}\sum\limits _{i=1}^n\sum\limits_{j=1}^m(a_i+b_j)^k\)

然后拆开就是 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^m\sum\limits_{t=0}^{k} a_i^tb_j^{k-t}\dbinom{k}{t}\)

调换一下枚举顺序就是 \(k!\sum\limits_{t=0}^kt!\sum\limits_{i=1}^na_i^t(k-t)!\sum\limits_{j=1}^mb_j^{k-t}\)

然后后面两个是卷积的关系,所以只需要快速求出 \(\sum\limits_{i=1}^na_i^k\)

这个在[THUPC2017] 小 L 的计算题 说过了。

P4389 付公主的背包

对于单个物品,转移方程的生成函数为 \(G(x)=\sum\limits_{i=0}^\infty [i\mod v=0]x^i\)

如果是多个就是把它们都乘起来。

\(G(x)=\sum\limits_{i=0}^\infty x^{iv} =\frac {1}{1-x^v}\)

依次乘起来是不行的,考虑取 \(\ln\) ,然后再 \(\exp\)

\(\ln (G(x))=F(x)\)

\(F'(x)=\frac {G'(x)}{G(x)}=(1-x^v)\sum\limits_{i=1}^\infty vix^{vi-1}\)

\(=\sum\limits_{i=1}^\infty vx^{vi-1}\)

然后把 \(F(x)\) 积分一下就行。 由于 \(F(x)\) 的项数为 \(\frac {n}{v}\),所以加法的复杂度为 \(O(m\ln n)\)

P2000 拯救世界

把所有状态下的生成函数都写出来.

\(F_1(x)=\frac {1}{1-x^6}\)

\(F_2(x)=\frac {1-x^{10}}{1-x}\)

\(F_3(x)=\frac{1-x^6}{1-x}\)

\(F_4(x)=\frac {1}{1-x^4}\)

\(F_5(x)=\frac {1-x^8}{1-x}\)

\(F_6(x)=\frac {1}{1-x^2}\)

\(F_7(x)=1+x\)

\(F_8(x)=\frac {1}{1-x^8}\)

\(F_9(x)=\frac 1 {1-x^{10}}\)

\(F_{10}(x)=\frac {1-x^4}{1-x}\)

我打这些玩应的意义是什么啊

总之你知道这些玩应乘起来得 \(\frac {1}{(1-x)^5}\)

然后这个东西等于 \(\sum \limits_{i=0}^\infty \dbinom{5+i-1}{i}x^i\)

这是个重要的结论(应该好多题都用

证明:

\(\frac {1}{(1-x)^n}=(1-x)^{-n}\)

根据广义二项式定理, \((1-x)^{-n}=\sum\limits_{i=0}^{\infty} \dbinom{-n}{i}{(-x)}^i\)

\(=\sum\limits_{i=0}^\infty \frac {(-n)(-n-1)(-n-2)\dots(-n-i+1)}{i}x^i\)

分子的所有式子都提出个负号。

\(=\sum\limits_{i=0}^\infty (-1)^i\dbinom{n+i-1}{i}(-x)^i\)

\(=\sum\limits_{i=0}^\infty \dbinom{n+i-1}{i}x^i\)

然后第 \(n\) 项系数就是 \(\dbinom{5+n-1}{n}=(n+1)(n+2)(n+3)(n+4)\)

所以还需要用 FFT 写个高精乘

P4841 [集训队作业2013]城市规划

\(n\) 个点的无向图的数量为 \(g_n\)\(n\) 个点的无向连通图的 \(f_n\)

显然,\(g_n=2^{\dbinom{n}{2}}\)

然后还有 \(\displaystyle g_n=\sum _{i=1}^{n} \dbinom {n-1} {i-1} f_i g_{n-i}\)

拆开式子

\(\displaystyle \frac{g_n}{(n-1)!}=\sum_{i=1}^n \frac{f_i}{(i-1)!} \frac{g_{n-i}}{(n-i)!}\)

设三个生成函数 : \(G(x)=\sum \limits _{i=1} \frac {g_i}{(i-1)!},H(x)=\sum\limits_{i=1} \frac {g_i}{i!},F(x)=\sum\limits _{i=1}\frac {f_i}{(i-1)!}\)

显然 \(F(x)=G(x)H^{-1}(x)\)

然后就能求 \(F(x)\),然后 \(f_n\) 也能解了。

CF438E The Child and Binary Tree

\(g_i\) 表示 \(i\) 是否在集合里,\(f_i\) 表示权值和为 \(i\) 的方案数

\(f_{n}=[n==0]+\sum\limits _{i=1}^n g_i\sum\limits_{i=1}^{n-i} f_jf_{n-i-j}\)

\(f_i\)\(g_i\) 的生成函数分别为 \(F(x)\)\(G(x)\)

显然可以看出 \(F=GF^2+1\)

然后有种让人想用求根公式的冲动,但是经过题解提醒发现 \(G(x)\) 不能求逆(零次项为零),所以只能硬解

同时乘 \(G\)

\(G^2F^2-GF+G=0\)

\((GF-\frac 1 2)^2=\frac 1 4 -G\)

\(GF=\frac 1 2 ±\sqrt{\frac 1 4-G}\)

观察发现当取正号时零次项不相同,所以舍掉留负号。

\(F=\frac {2}{1+\sqrt{1-4G}}\)

[HAOI2018]染色

看着“恰好” 就知道是二项式反演www

\(g(i)\) 表示钦定 \(i\) 种颜色恰好 \(S\) 次。

\(g_i=\dbinom m i \frac {n!}{S!(n-iS)!}(m-i)^{n-iS}\)

首先从 \(m\) 种颜色中选出 \(i\) 种,然后从全排列中删掉钦定的相同颜色和不考虑的,然后再让不考虑的全排列

\(f_{i}\) 表示恰好 \(i\) 种出现 \(S\)

二项式反演

\(f_i=\sum\limits_{j=i}^m(-1)^{j-i}\frac {j!}{(j-i)!i!}g_j\)

\(=i!\sum\limits_{j=i}^{m} \frac {(-1)^{j-i}}{(j-i)!}g_jj!\)

\(=i!\sum\limits_{j=0}^{m-i}\frac {(-1)^j}{j!}g_{j+i}(j+i)!\)

然后把其中一个反转一下就能卷积了。


感觉好多套路题www

还没做到智慧的部分

好困


[MtOI2018]情侣?给我烧了!(加强版)

我终于写到这题了hhh

6Bit :谁说情侣必须是一男一女的?

这个梗我看一次乐一次,我笑点好低233

然而这个梗诞生的时候是讲二项式反演,但是我当时根本没写这题,后来咕咕咕了大半年才补上

困了不想写了,咕咕咕。

以下为正文。

首先是简化版:

考虑二项式反演 ,设 \(f_k\) 为钦定 \(k\) 对情侣卿卿我我的方案数。

\(f_k=\dbinom{n}{k}\dbinom{n}{k} k!2^k(2n-2k)!\)

根据二项式反演。

\(g_k\) 表示恰好 \(k\) 对情侣和和睦睦的方案数。

\(g_k=\sum\limits_{i=k}^n \dbinom{i}{k} (-1)^{i-k} f_k\)

然后化简

\(g_{k}=\sum\limits_{i=k}^n\dbinom{i}{k}(-1)^{i-k}\dbinom{n}{i}\dbinom{n}{i}i!2^i(2n-2i)!\)

\(g_k=\sum\limits_{i=k}^n (-1)^{i-k}\frac{i!n!n!}{k!(i-k)!(n-i)!i!(n-i)!i!}i!2^i(2n-2i)!\)

\(g_k=(n!)^2\sum\limits_{i=k}^n (-1)^{i-k} \frac {1}{k!(i-k)!((n-i)!)^2}2^i(2n-2i)!\)

然后平移 \(i\)

\(g_k=\frac {(n!)^2}{k!}\sum\limits_{i=0}^{n-k} \frac {(-1)^i2^i}{i!} \frac {(2n-2i-2k)!}{((n-i-k)!)^2}\)

这是个卷积的形式,但是显然即使你不会卷积这个也能过简化版。

然后是强化版。

\(G(x)\)\(g_i\) 的生成函数,\(F(x)\)\(f_i=\frac {{(-1)^i}2^i}{i!}\) 的生成函数,\(H(x)\)\(h_i=\frac {(2i)!}{(i!)^2}\) 的生成函数。

然后发现最终答案就是 \(F(x)H(x)\) 再成上个系数。

然后大家都是说 \(F(x)\) 是显然的,但是我不会wwww

我:为啥 \(\frac {1}{n!}\) 的生成函数的封闭形式是 exp 啊

红太阳:你直接泰勒展开就完事了

我:啊?啊......啊!

\(e^x\)\(x=0\) 处泰勒展开,得到 \(\sum\limits _{i=0}^\infty \frac {e^0}{i!} x^i\)

所以就是 \(\sum\limits _{n=0}^\infty \frac {1}{n!}x^n\)

所以上面的 \(F(x)\) 的封闭形式是 \(e^{-2x}\)

不好整的是 \(H(x)\)

\(H(x)=\sum\limits_{i=0}^\infty \dbinom{2i}{i} x^i\)

然后需要用到一个高妙的等式: \(\frac {\dbinom{2n}{n}}{4^n}=\dbinom{n-\frac {1}{2}}{n}\)

这个等式我在之后处理一些长相丑陋的二项式经常用到。不过当时都是反着用的所以现在想不起来。

\(\dbinom {n-\frac {1}{2}}{n} = \frac {(n-\frac{1}{2})(n-\frac{1}{2}-1)\dots(n-\frac{1}{2}-n+1)}{n!}=\frac {(n-1)(n-3)(1)}{n!}2^n=\frac {(n-1)(n-3)\dots(1)\times (2)(4)\dots(2n)}{(n!)^2}4^n=\dbinom{2n}{n}4^n\)

所以有上式成立

所以有 \(H(x)=\sum\limits_{n=0}^\infty{ \dbinom {n-\frac1 2}{n}4^nx^n}\)

\(=\sum\limits_{n=0}^\infty\dbinom{-\frac 1 2}{n} 4^n x^n (-1)^n\)(理由是把里面拆开,同时提出负号)\(=(1-4x)^{-\frac 1 2}\)\(=\frac {1}{\sqrt {1-4x}}\)

然后把两个生成函数乘到一起

\(F(x)H(x)=\frac {e^{-2x}}{\sqrt {1-4x}}\)

完蛋求导我写不明白。

\(D(x)=\frac {e^{-2x}}{\sqrt{1-4x}}\)

\(D^2(x)=\frac {e^{-4x}}{1-4x}\)

\(D^2(x)(1-4x)=e^{-4x}\)

同时求导

\((D^2(x))'(1-4x)-4D^2(x)=(e^{-4x})'\)

\((2D'(x)D(x))(1-4x)-4D^2(x)=-4e^{-4x}\)

\(2D'(x)D(x)-8xD'(x)D(x)-4D^2(x)=-4D^2(1-4x)\) (等式右边是将开始的那个东西带进去了)

化简一下:

\(D'(x)-4xD'(x)=8xD(x)\)

然后写成递推公式的形式就是:\(d_{n+1}(n+1)-4nd_nx^n=8d_{n-1}x^n\)

\(nd_n=4d_{n-1}(n-1)+8d_{n-2}\)

然后就是推一下,最后的结果为 \(\frac {(n!)^2}{k!}d_{n-k}\)

【CSGRound2】开拓者的卓识

第一步,首先考虑的是每个点对答案的贡献。

也就是转化成每个点,开始向外扩展 \(k\) 步的方案数

也就是说 \(ans_{1,i}=\sum\limits_{j=1}^i a_{j} \dbinom{j+k-1}{k-1} \dbinom{i-j+k}{k-1}\)

\(A_j=a_j\dbinom{j+k-1}{k-1}\)\(B_j=\dbinom{j+k}{k-1}\)

然后卷起来

[AGC005F] Many Easy Problems

巧妙的思路。

第一个巧妙的思路是假设 \(c_{(i,j)}\) 表示 \(i\) 节点为大小为 \(j\) 的点集可以做出的贡献。这样 \(f(k)\) 为 $\sum c_{(i,k)}。

然后考虑求解。如果一个点集形成的联通块包含 \(i\) ,就说明 \(i\) 能造成贡献。 所以凡是选择的点集仅仅在 \(i\) 的某一个儿子的子树内,或者在整个以 \(i\) 为根的子树外,就不能形成贡献。

所以: \(c_{(i,j)}=\dbinom{n}{j}-\sum\limits_{v\in son_i} \dbinom{siz_v}{j}-\dbinom{n-siz_i}{j}\)

然后你发现还是不会。这就要继续转化:考虑把相同子树大小的点合并到一起,计算贡献,因为它们形成的组合数是相同的

然后设 \(cnt_i\) 表示子树大小为 \(i\) 的点的数量。\(cnt'_i\) 表示子树大小为 \(n-siz_i\) 的点的数量。所以设 \(c_{i,j}\) 的和为 \(c_j\)

\(\displaystyle c_j=n\dbinom{n}{j}-\sum_{j\leq i<n}(\dbinom{i}{j}cnt_i+\dbinom{i}{j}cnt'_i)\)

\(\displaystyle =n\dbinom{n}{j}-\sum_{j\leq i <n}\frac{i!}{j!(i-j)!} (cnt_i+cnt'_i)\)

\(\displaystyle=n\dbinom{n}{j}-\frac {1}{j!}\sum_{0\leq i<n-j}\frac{(i+j)!(cnt_{i+j}+cnt'_{i+j})}{j!}\)

左边把一个式子转一下就是卷积

P4173 残缺的字符串

伪装成字符串的贩毒题()

据说是套路。

假设两个字符串的匹配函数 \(dis(A,B)\),当他们为 \(0\) 说明能完全匹配上,否则不行。

然后 \(dis(A,B)=\sum\limits_{0\leq i<n} (A[i]-B[i])^2A[i]B[i]\)

前面的平方项的意思是强制转正,防止正负抵消。乘上当前字符是如果有一个为通配符,将通配符设为 \(0\),乘进去可以认定为相等。

然后设 \(f(i)\) 表示第 \(i\) 位开始是否能完全匹配,\(f(i)=dis(A,B[i,i+m-1])\)

\(f(i)=\sum\limits_{0\leq j <m} (A[j]-B[i+j])^2A[j]B[i+j]\)

手动展开 \(f(i)=\sum\limits_{0\leq j<m} A[j]^3B[i+j]-2\sum\limits_{0\leq j} A[j]^2B[j+i]^2+\sum\limits_{0\leq j}A[j]B[i+j]^2\)

这是三个典型的卷积形式。所以就做出来了

U107257 【模板】二维 FFT

不懂原理。但是好象是因为 FFT 是线性变换。 所以先对行 DFT,然后对列 DFT,然后整体乘起来,然后对列 IDFT,再对行 IDFT

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4000;
namespace POLY
{
	ll A[maxn],B[maxn],pos[maxn],S[maxn];
	const int mod=998244353,g=3,ginv=332748118;
	ll ksm(ll x,int y)
	{
		ll ret=1;
		while(y)
		{
			if(y&1) ret=ret*x%mod;
			x=x*x%mod; y>>=1;
		}
		return ret;
	}
	int pre(int n,int m)
	{
		int lim=0;
		while((1<<lim)<=n+m) lim++;
		for(int i=0;i<=(1<<lim);i++)
				pos[i]=(pos[i>>1]>>1)|((i&1)<<(lim-1));
		return lim;
	}
	void NTT(ll *f,ll n,int opt)
	{
		for(int i=0;i<n;i++) if(i<pos[i]) swap(f[i],f[pos[i]]);
		for(int i=1;i<n;i<<=1)
		{
			ll step=ksm((opt>0?g:ginv),(mod-1)/(2*i));
			for(int j=0;j<n;j+=i+i)
			{
				ll cur=1;
				for(int k=j;k<j+i;k++)
				{
					int gx=f[k],hx=1ll*cur*f[k+i]%mod;
					f[k]=(gx+hx)%mod;f[k+i]=(gx-hx+mod)%mod;
					cur=cur*step%mod;
				}
			}
		}
		if(opt==-1)
		{
			int ninv=ksm(n,mod-2);
			for(int i=0;i<n;i++) f[i]=f[i]*ninv%mod;
		}
	}
}
int n;
using POLY::mod;
ll f[maxn][maxn],g[maxn][maxn],F[maxn][maxn],G[maxn][maxn],ans[maxn][maxn],anss[maxn][maxn];
int main()
{
	scanf("%d",&n);
	for(int i=0;i<=n;i++) 
	{
		for(int j=0;j<=n;j++)
			scanf("%lld",&f[i][j]);
	}
	for(int i=0;i<=n;i++) 
	{
		for(int j=0;j<=n;j++)
			scanf("%lld",&g[i][j]);
	}
	int len=(1<<POLY::pre(n,n));
	for(int i=0;i<=n;i++) POLY::NTT(f[i],len,1),POLY::NTT(g[i],len,1);
	for(int i=0;i<=len;i++)
	{
		for(int j=0;j<=len;j++)
		{
			F[j][i]=f[i][j]; G[j][i]=g[i][j];
		}
	}
	for(int i=0;i<=len;i++) POLY::NTT(F[i],len,1),POLY::NTT(G[i],len,1);
	for(int i=0;i<=len;i++)
	{
		for(int j=0;j<=len;j++) 
			ans[i][j]=F[i][j]*G[i][j]%mod;
		POLY::NTT(ans[i],len,-1);
		for(int j=0;j<=len;j++)
			anss[j][i]=ans[i][j];
	}
	for(int i=0;i<=len;i++)
	{		
		for(int j=0;j<=len;j++)
			anss[j][i]=ans[i][j];
	}
	for(int i=0;i<=len;i++) POLY::NTT(anss[i],len,-1);
	for(int i=0;i<=2*n;i++)
	{
		ll ret=0;
		for(int j=0;j<=len;j++) ret^=anss[i][j];
		printf("%lld ",ret);
	}
	printf("\n");
	for(int i=0;i<=2*n;i++)
	{
		ll ret=0;
		for(int j=0;j<=len;j++)
			ret=ret^anss[j][i];
		printf("%lld ",ret);
	}
	printf("\n");

}

[ABC265Ex] No-capture Lance Game

6Bit:这题你们自己研究一下吧。

我记得当时被另外一个高妙的做法折磨傻了。

两个车放置的方式不同做法肯定不同。

如果他们全是对向放置的,那就是普普通通的 Nim 游戏。

如果是反向放置的,那就是磨谁的步数多。步数多的即使 Nim 游戏输了也能吧对方磨死。

所以设方程为 \(f[i][j][k]\) 表示做了 \(i\) 行,对向的部分异或和为 \(j\),反向的部分 A 比 B 少 \(k\)

转移就是 \(f[i][j][k]->f[i+1][j\oplus (x-y)][k](x>y)\\->f[i+1][j][k+(n-y)-(x-1)](x<y)\)

\(x,y\) 分别为两人的棋子的位置。

然后容易发现的是第二维是个异或卷积,第三位也是个卷积。所以这就是二维 FFT 的用处。而行之间的转移可以用矩阵快速幂。还有一种就是把行和列 DFT 完了之后都变成 \(k\) 次幂再乘起来效果是一样的

P5110 块速递推

手推斐波那契数列。在上面 P4451 [国家集训队]整数的lqp拆分 这题写过了,不写了。

[CF827E]Rusty String

我们可以用上一道字符串匹配中用到的套路,设 \(F(i)\) 表示前段长度为 \(i\) 时是否能匹配上。(0 为能匹配,其他均为不行)

\(F(i)=\sum\limits_{1\leq j\leq n-i} A[i+j]A[j](A[i+j]-A[j])^2\)

然后等式拆开再卷积三次就行。

但是这题不行。因为这里我们把 ? 看作通配符,这样可能同一位置的两个 ? 与不同的字符匹配。这样不符合题意。

我们可以找到一个性质:如果前端长度为 \(i\) 的是矛盾的,那么前端长度为 \(i\) 的倍数的一定存在不行的。

具体的可以看这张图:

如果是河里的,那么同种颜色表示的字符一定是相同的。错开成 \(i\) 的倍数之后也还是让红色和绿色的分别对应,也就是说一定也河里。

如果一种颜色中出现了两种确定的不相同的字符,那么一定能做到错开之后让不同字符对应到一起使其不合理。

所以排除掉矛盾的方式就是找到所有倍数,如果有至少一个是算出来就不行的,那么这个就一定不行。\(O(n\ln n)\) 判断。

P5075 [JSOI2012] 分零食

\(f(i)\) 为分到 \(i\) 块糖的小朋友的幸福度。求法写在题面了。

假设 \(f(i)\) 的生成函数为 \(F(x)\),那么 \((F(x)-1)^k\)有k个分到了糖的小盆友的总幸福值。

所以 \(ans=\sum\limits_{i=0}^{A} (F(x)-1)^i\)

\(=\frac{1-(F(x)-1)^A}{2-F(x)}\)

没了,然后里层 NTT 要用 998244353 取模,外层用给的 P ,给我恶心到了。

P5395 第二类斯特林数·行

第二类斯特林数:把 \(n\) 个不同元素分进 \(m\) 个相同的集合中的方案数。

记为 \(\begin{Bmatrix}n\\m\end{Bmatrix}\)

这玩意有个最重要(?)的性质:

\(m^n=\sum\limits_{i=1}^n \dbinom {m}{i} i! \begin{Bmatrix} n\\i\end{Bmatrix}\)

很容易理解,就是把 \(n\) 个不同的元素乱放在 \(m\) 个不同的盒子,从 \(m\) 个盒子中选 \(i\) 个,\(i!\) 是给这 \(i\) 个不同的顺序,\(\dbinom m i\) 是从 \(m\) 个选 \(i\) 个非空盒子

根据二项式反演: \(\begin{Bmatrix}n\\m\end{Bmatrix}\)$ =\frac {1}{m!}\sum\limits _{i=1}^{m} (-1)^{m-i}\dbinom m i i^m $

\(\begin{Bmatrix} n \\ m\end{Bmatrix} =\sum\limits_{i=1}^m \frac {(-1)^{m-i}}{(m-i)!} \frac{i^m}{i!}\)

这是卷积形式,可以 NTT 做了。

P5396 第二类斯特林数·列

设第二类斯特林数的生成函数为 \(F_k(x) =\sum\limits _{i=0}^{\infty}\)\(\begin{Bmatrix}i\\k\end{Bmatrix}\)\(x^i\)

根据第二类斯特林数的性质: \(F_k(x)=\sum\limits_{i=0}^\infty (k\begin{Bmatrix}{i-1}\\k\end{Bmatrix}+\begin{Bmatrix}i-1\\k-1\end{Bmatrix}) x^i\)

\(F_k(x) =(kF_k(x)+F_{k-1}(x))x\)

\(F_{k}(x)=\frac {x} {1-kx} F_{k-1}(x)\)

因为 \(F_0(x)=1\)

所以就是 \(F_m(x)=\frac{x^m}{\prod _{i=1}^m (1-ix)}\)

分治 NTT 即可。

P4091 [HEOI2016/TJOI2016]求和

推式子推几步就行((

\(=\displaystyle \sum_{j=0}^n 2^j (j!)\sum_{i=j}^n\begin{Bmatrix}i\\j\end{Bmatrix}\)

因为当 \(i<j\) 时, \(\begin{Bmatrix} i\\j\end{Bmatrix}=0\),所以可以直接改条件

\(=\displaystyle \sum_{j=0}^n 2^j (j!)\sum_{i=0}^n\begin{Bmatrix}i\\j\end{Bmatrix}\)

根据咱们刚才推的式子

\(=\displaystyle \sum_{j=0}^n 2^j j!\sum_{i=0}^n \sum_{k=0}^j \frac {(-1)^{j-k}k^i}{(j-k)!k!}\)

\(=\displaystyle \sum_{j=0}^n 2^j j! \sum_{k=0}^j\frac {(-1)^{j-k}\sum_{i=0}^nk^i}{(j-k)!k!}\)

\(\displaystyle =\sum _{j=0}^n2^j j!\sum_{k=0}^j\frac {(-1)^{j-k}}{(j-k)!}\frac {k^{n+1}-1}{k!(k-1)}\)

然后后面卷积,相乘一下就行。

P3760 [TJOI2017] 异或和

有趣的转化。求异或和就是所有出现奇数次的数字的异或和

这意味着我们要求所有数字在连续和中出现的次数。

\(f_{i}\) 为前缀和中所有数字连续出现的次数。 \(g_{i}\) 表示连续和中所有数出现的次数。

\(g_i=\sum_{j=0}^mx f_{j} f_{j+i}\)

反转一下就是卷积的形式。

[CF1613F]Tree Coloring

简单题,但是 \(O(n\log ^2 n)\)

假设钦定 \(i\) 个点不合法的方案数为 \(f_i\),求出来 \(f_i\) 容斥一下就是答案。

最重要的性质就是一个点只能钦定一个儿子不合法。 并且所有点之间选哪个儿子互不影响。

假设我们钦定了 \(i\) 个点不合法,也就是 \(i\) 条边不合法,这样会形成 \(n-i\) 个联通块。而染色的方案数就是联通块的个数的阶乘乘以选择 \(i\) 个点的方案数。

假设一个点有 \(k\) 个儿子,那么设 \(f_i\)的生成函数为 \(F(x)\),只考虑选这个点的儿子的 \(F(x)=kx+1\),那么整棵树就是把所有的 \(F(x)\) 乘起来。

然后就能分治 NTT 了。

[ABC267Ex] Odd Sum

观察到一点就是 \(A_i\) 极小,也就是一定有一堆重复元素。然后假设 \(f_{i,j,0/1}\) 为元素为 \(i\),和为 \(j\),选出偶数/奇数个元素的方案数。

把所有的十个卷积乘起来就是答案,乘起来就是奇数卷奇数加偶数卷偶数就是新的偶数,奇数卷偶数加偶数卷奇数就是新的奇数。

需要分治进行计算。

[ABC269Ex] Antichain

我觉得这道题和动态 DP 的想法很像()

\(f(i,j)\) 为以 \(i\) 为根的子树选 \(j\) 个点的方案数,\(F_i(x)\) 为它的生成函数。因为一个点要么分别选子树里面的,要么只选自己,所以 $F_i(x)=x+\prod\limits_{v\in son} F_v(x) $

乘法可以用 NTT 优化,但是复杂度还是错误的,对于链或者菊花,要合并 \(O(n)\)次,很容易变成 \(O(n^2)\)

那么考虑动态 DP 的优化方式,设 \(G(x)\) 为只考虑轻儿子的方案。\(G(x)\) 是通过暴力合并重儿子的 \(F(x)\) 计算的,因为轻链最多 \(\log\) 个,所以这里的复杂度为 \(O(n\log ^2 n)\)

那么考虑如何计算每一条重链链头的 \(F(x)\)

\(F_1(x)=G_1(x)(F_2(x))+x\\=G_1(x)(G_2(x)F_3(x)+x)+x\\\dots\\=G_1(x)(G_2(x)\dots (G_c(x)+x)\dots+x)+x\)

这里把下标换成重链链头的深度,方便理解。 \(c\) 是重链长度。

然后手动展开一下

\(\displaystyle F_1(x)=x(1+\sum_{i=1}^{c} \prod_{j=i}^n G_j(x))+\prod_{i=1}^n G_i(x)\)

这里可以分治 NTT 的过程中维护两个多项式,一个表示前缀积的和,另一个表示所有的积。乘的时候合并就行了。

注意方向就行。

我觉得我现在写多项式越来越丑了,但是 vector 真香。

有点想不通为啥红太阳写那么短()

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2000030;
namespace POLY
{
	ll A[maxn],B[maxn],pos[maxn],S[maxn];
	const int mod=998244353,g=3,ginv=332748118;
	ll ksm(ll x,int y)
	{
		ll ret=1;
		while(y)
		{
			if(y&1) ret=ret*x%mod; 
			x=x*x%mod; y>>=1;
		}
		return ret;
	}
	int pre(int n,int m)
	{
		int lim=0;
		while((1<<lim)<=n+m) lim++;
		for(int i=0;i<=(1<<lim);i++)
				pos[i]=(pos[i>>1]>>1)|((i&1)<<(lim-1));
		return lim;
	}
	void NTT(ll *f,ll n,int opt)
	{
		for(int i=0;i<n;i++) if(i<pos[i]) swap(f[i],f[pos[i]]);
		for(int i=1;i<n;i<<=1)
		{
			ll step=ksm((opt>0?g:ginv),(mod-1)/(2*i));
			for(int j=0;j<n;j+=i+i)
			{
				ll cur=1;
				for(int k=j;k<j+i;k++)
				{
					int gx=f[k],hx=1ll*cur*f[k+i]%mod;
					f[k]=(gx+hx)%mod;f[k+i]=(gx-hx+mod)%mod;
					cur=cur*step%mod;
				}
			}
		}
		if(opt==-1)
		{
			int ninv=ksm(n,mod-2);
			for(int i=0;i<n;i++) f[i]=f[i]*ninv%mod;
		}
	}
	void MUL(ll *s,ll *f,ll *h,int n,int m)
	{
		int lim=pre(n,m);
		for(int i=0;i<=n;i++) A[i]=f[i];
		for(int i=0;i<=m;i++) B[i]=h[i];
		for(int i=n+1;i<=(1<<lim);i++) A[i]=0;
		for(int i=m+1;i<=(1<<lim);i++) B[i]=0;
		NTT(A,(1<<lim),1);NTT(B,(1<<lim),1);
		for(int i=0;i<=(1<<lim)-1;i++) s[i]=A[i]*B[i]%mod;
		NTT(s,(1<<lim),-1);
	}
}
using POLY::mod; using POLY::ksm; 
int n;
int to[maxn],nxt[maxn],head[maxn],num;
void add(int x,int y){num++;to[num]=y;nxt[num]=head[x];head[x]=num;}
int son[maxn],top[maxn],fa[maxn],siz[maxn];
void FIT(vector<ll> &f)
{
	int cnt;
	for(int i=0;i<f.size();i++) 
		if(f[i]) cnt=i; 
	f.resize(cnt+1);
}
void dfs1(int p,int f)
{
	fa[p]=f; siz[p]=1;
	for(int i=head[p];i;i=nxt[i])
	{
		if(to[i]==f) continue;
		dfs1(to[i],p);
		siz[p]+=siz[to[i]]; 
		if(siz[to[i]]>siz[son[p]]) son[p]=to[i];
	}
}
void dfs2(int p,int tp)
{
	top[p]=tp;
	if(son[p]) dfs2(son[p],tp);
	for(int i=head[p];i;i=nxt[i])
	{
		int v=to[i]; if(v==fa[p]||v==son[p]) continue;
		dfs2(v,v);
	}
}
vector<ll> solve(int l,int r,vector<vector<ll>> &G)
{
	if(l>r) {vector<ll> ret; ret.push_back(1); return ret;}
	if(l==r) return G[l]; 
	int mid=(l+r)>>1;
	vector<ll> L,R,ans; L=solve(l,mid,G); R=solve(mid+1,r,G);
	ans.resize(1<<POLY::pre(L.size(),R.size())); 
	POLY::MUL(ans.data(),L.data(),R.data(),L.size()-1,R.size()-1);
	FIT(ans);
	return ans; 
}
struct martix{vector<ll> L,R;};
martix solve_heavy(int l,int r,vector<vector<ll>> &G)
{
	if(l>r){martix ret; ret.L.push_back(1); ret.R.push_back(1); return ret;}
	if(l==r)
	{
		vector<ll> x; x.push_back(1);
		return (martix){x,G[l]};
	}
	int mid=(l+r)>>1;
	martix Lans,Rans,ret;
	Lans=solve_heavy(l,mid,G); Rans=solve_heavy(mid+1,r,G);
	ret.L.resize(max(1ll<<POLY::pre(Lans.L.size(),Rans.R.size()),(ll)Rans.L.size()));
	ret.R.resize(1<<POLY::pre(Lans.R.size(),Rans.R.size()));
	POLY::MUL(ret.L.data(),Lans.L.data(),Rans.R.data(),Lans.L.size()-1,Rans.R.size()-1);
	for(int i=0;i<(int)Rans.L.size();i++)
		ret.L[i]=(ret.L[i]+Rans.L[i])%mod;
	POLY::MUL(ret.R.data(),Lans.R.data(),Rans.R.data(),Lans.R.size()-1,Rans.R.size()-1);
	FIT(ret.L); 
	FIT(ret.R);
	return ret;
}
vector<ll> dfs3(int u)
{
	vector<vector<ll>> light_son,heavy;
	vector<ll> G;
	int p=u;
	while(p)
	{
		light_son.resize(0);
		for(int i=head[p];i;i=nxt[i])
		{
			int v=to[i]; 
			if(v==fa[p]||v==son[p]) continue;
			light_son.push_back(dfs3(to[i]));
		}
		G=solve(0,light_son.size()-1,light_son);
		FIT(G);
		heavy.push_back(G);		
		p=son[p];
	}
	reverse(heavy.begin(),heavy.end());
	martix tmp=solve_heavy(0,heavy.size()-1,heavy);
	FIT(tmp.L);FIT(tmp.R);
	vector<ll> ret; ret.resize((1<<POLY::pre(tmp.L.size(),2))+10);
	vector<ll> xx; xx.resize(2); xx[0]=0,xx[1]=1;
	POLY::MUL(ret.data(),tmp.L.data(),xx.data(),tmp.L.size()-1,1);
	for(int i=0;i<(int)tmp.R.size();i++) 
		ret[i]=(ret[i]+tmp.R[i])%mod;
	FIT(ret);
	return ret;
}
int main()
{
	//freopen("p.in","r",stdin);
	//freopen("p.out","w",stdout);
	scanf("%d",&n);
	for(int i=2;i<=n;i++)
	{
		int x;scanf("%d",&x);
		add(x,i); 
	}
	dfs1(1,1); dfs2(1,1);
	vector<ll> ans=dfs3(1);
	ans.resize(n+2);
	for(int i=1;i<=n;i++)
		printf("%lld\n",ans[i]);
	fprintf(stderr,"Time : %.3lf\n",1.0*clock()/CLOCKS_PER_SEC);
}

P5219 无聊的水题 I

昨天做这道题有点晕,今天 Tsawke 问了一个问题,我受到启发,然后这道题和下一个题就瞬间明白了。

首先是前置知识(ZSH:你看一眼 oi-wiki 就行):\(prufer\) 序列。

构造方式就是删掉标号最小的叶子(只有一个儿子的根也算),然后在序列里记上它的父亲。直到剩下两个节点。

显而易见的性质:每个点会出现度数 -1 次。能确定一个唯一的树。

然后就简单了。把 \(n\) 个数可以填多次,填进 \(n-2\) 个位置,每个数不能出现超过 \(m-1\) 次。

然后用 EGF 解决多重排列计数,把 \(\sum\limits_{i=0}^{m-1}\frac {x^i}{i!}\)\(n-2\) 次,然后再乘上 \((n-2)!\)

这就是多重集排列计数的解法。

Tsawke 问的就是这个。

P5339 [TJOI2019]唱、跳、rap和篮球

我昨天做完了洛谷上的两道 cxk 题。

考虑容斥。答案为:\(\displaystyle \sum_{i=0}^{n}(-1)^i \dbinom{n-3i}{i} S(n-3,a-i,b-i,c-i,d-i)\)

\(S(n,a,b,c,d)\) 为喜好分别为 \(a,b,c,d\) 的人排列在 \(n\) 个位置上的方案数。这就是上面的多重集排列。

首先考虑到只有四种字符。所以可以分四次来做。

对于每一种字符,我们定义 \(f(i)\) 为在 \(S\) 中的每 \(i\) 位置是否可以匹配。\(f(i)\) 就是如果相邻 \(k\) 个有当前字符就是 \(1\),否则是 \(0\)\(g(i)\) 就是 \(T\) 中这一位是否是当前字符。然后求的就是 \(\sum\limits_{x=0}^m \sum\limits_{i=0}^m f(i+x)g(i)\) 翻转过来就是卷积。

然后如果答案第 \(x\) 位为 \(T\) 中含有当前字符的个数,就说明 \(x\) 位在当前字符下可以。如果 \(x\) 在四种字符下都可以,那么 \(x\) 就是可以的。

P2791 幼儿园篮球题

吐槽:其实我挺烦这个梗的。我弟这两天总看各种 cxk 鬼畜,现在看着这个梗就反胃。

要求:\(\displaystyle \sum_{i=1}^k \dbinom{m}{i} \dbinom{n-m}{k-i} i^L\)

首先是经典斯特林数的式子。

\(\displaystyle m^n= \sum_{i=1}^m \begin{Bmatrix}n\\i\end{Bmatrix} \dbinom{m}{i} i!\)

代入到下面。

\(\displaystyle =\sum_{i=1}^k \dbinom{m}{i} \dbinom{n-m}{k-i}\sum_{j=1}^i \begin{Bmatrix}L\\j\end{Bmatrix}\dbinom{i}{j} j!\\ =\sum_{j=1}^k \begin{Bmatrix}L\\j\end{Bmatrix} j!\sum_{i=j}^k \dbinom{m}{i}\dbinom{n-m}{k-i}\dbinom{i}{j}\\=\sum_{j=1}^k \begin{Bmatrix}L\\j\end{Bmatrix} j! \dbinom{m}{j} \sum_{i=j}^k \dbinom{m-j}{i-j}\dbinom{n-m}{k-i}\\= \sum_{j=1}^k \begin{Bmatrix}L\\j\end{Bmatrix} j! \dbinom{m}{j} \sum_{i=0}^{k-j} \dbinom{m-j}{i} \dbinom{n-m}{k-i-j}\)

然后利用范德蒙德卷积:\(\displaystyle \sum \dbinom{n}{i} \dbinom{m}{k-i}=\dbinom{n+m}{k}\)

\(\displaystyle =\sum_{j=1}^k \begin{Bmatrix}L\\j\end{Bmatrix}j!\dbinom{m}{j}\dbinom{n-j}{k-j}\)

这就是答案。

CF923E Perpetual Subtraction

懒得打公式捏。

IMG_8358.JPG

IMG_8359.JPG

#loj6289. 花朵

我做过一个跟这个几乎一模一样的题,但是还是写了好长时间/kk

菊花的转移我觉得我挺明白的,不过链上的当时也没想明白。

这个呢就是每个点有两个方程 \(F[i][j],G[i][j]\),一个表示自己没选,另一个表示自己选了。然后用矩阵转移的话需要考虑头尾分别是什么

这样比较好理解了。

还有点细节就是分治 NTT 时不要带着个 stl 的大数组下传进去,(复杂度好像会假)

#loj6672. 「XXOI 2019」惠和惠惠和惠惠惠

强调一下最后一次必为 \(0\)。题面有误

这种数居然有名字?
首先就是 \(f_{i,j}\) 表示前 \(i\) 个回合,正好 \(j\) 次正好为 \(0\)

\(f_{i,j}=\sum\limits_{k=0}^{i-1} f_{k,j-1}h(i-k)\) \(h(i)\) 表示走了 \(i\) 步,只有头和尾正好碰到 \(0\) 的方案数。只要知道了 \(h(i)\) 那只要乘 \(k\) 次即可。

现在考虑怎么求 \(h(i)\) 我们先固定第一步和最后一步,剩下的就可以随便碰到 \(0\) 了,这样我们求出来后需要右移 \(2\) 位。

那么 \(h(i) =\sum \limits_{j=0}^i \dbinom i j D(i-j)\) 意思就是选出来 \(j\) 步走 \(0\)\(D(i)\) 是卡特兰数。

这样就求完了。巨大卡常很烦人。

[ABC279Ex] Sum of Prod of Min

我们假设 \(F_k(x)\) 表示在位置 \(k\) 上的生成函数。

\(F_k(x)=\sum_\limits{i=0}^{\infty} \min(t,i)x^i\)

\(=\sum\limits_{i=0}^{t-1}ix^i+\sum\limits_{i=t}^{\infty} \frac {x(1-x^t)}{(1-x)^2}\)

(这一步就是拿等比数列暴力合并,题解大佬都是妙妙求导但我不会/kk)

\(ans=\prod\limits_{i=1}^n F_{i}(x)\)

\(=\prod\limits_{i=1}^n\frac {x(1-x^i)}{(1-x)^2}\)

\(=(1-x)^{-2n}x^n\prod \limits_{i=1}^n (1-x^i)\)

这里涉及到一个小知识

五边形数定理:

\(\prod\limits_{i=1}^n (1-x^i) =\sum\limits_{i=0}(-1)^ix^{\frac {i(3i\pm 1)}{2}}\)

证明:不会。(欧拉本人在过了十年之后才证明出来

然后继续化简:

由于我们想要求次数为 \(m\) 的项,并且前面有个比较烦的 \(x^n\),所以我们干脆转化为求次数为 \(m-n\) 的项。

\(=[x^{m-n}]\sum\limits_{i=0} \dbinom{i+2n-1}{i} x^i \sum\limits_{j=0}^{\infty} f(j)x^{a(j)}\)

我们假设 \(f(i),a(i)\) 为五边形定理的系数和指数。

这里还用到一个知识点

上指标反转:\(\dbinom r k=\dbinom {k-r-1}{k}(-1)^k\)

证明:

\(\dbinom r k=\frac {r^{\underline{k}}}{k!}=\frac {r(r-1)\dots (r-k+1)}{k!}=(-1)^k \dbinom{k-r-1}{k}\)

我怎么才想起来我在情侣那个题用过这个

然后直接找指数为 \(m-n\) 的项。

\(=\sum\limits_{j=0}^{\infty} f(j) \dbinom{m-n-a(j)+2n-1}{m-n-a(j)}\)

这个就可以做啦。

posted @ 2023-01-03 23:03  cc0000  阅读(141)  评论(0编辑  收藏  举报