L Lndjy and the mex
题意:给定长度为 n 的序列 {ai},满足 ∑ai=n。问由 a0 个 0,a1 个 1,……,an 个 n 构成的全部 ∏i=0nai!n! 个序列中,每个序列的全部连续子序列的 mex 值之和。n≤1×105。
解法:显然直接求 mex 是非常不好做的,尝试将其转化为方案数。设 fi 表示连续子序列的 mex 值为 i 的连续子序列个数,则答案为 i=0∑+∞ifi,令 gi=j=i∑+∞fj,则有:
i=0∑+∞ifi==i=1∑+∞i(gi−gi+1)i=1∑+∞gi
以上的化简是用到了 g+∞=0。
注意到 gi 的组合意义为,连续子序列 mex 值大于等于 i 的序列个数,等价于 0∼i−1 的数字至少出现一次,其余数字随意的方案数。考虑到每个数字选了多少个,假设第 i 个数字选了 bi 个,那么有:
gi=b0=1∑a0b1=1∑a1⋯bi−1=1∑ai−1bi=0∑aibi+1=0∑ai+1⋯bn=0∑an(b0,b1,⋯,bn∑j=0nbj)(a0−b0,a1−b1,⋯,an−bnn−∑j=0nbj)(n−j=0∑nbj+1)
即首先是连续子序列内部的排列,再是外部的排列,最后再将这个连续子序列插入完整的序列。那么这个问题就可以非常容易的使用 EGF 得到解决:设 fi(x) 表示数字 i 的选择方案的 EGF,fi(x)=j=0∑aij!(ai−j)!xj,gi(x)=fi(x)−ai!1 表示至少选一个的方案,则最终的答案为:
s=0∑n(n−s+1)!(s![xs]i=0∑n−1j=0∏igj(x)j=i+1∏nfj(x))
注意:此处和通常的 EGF 稍有区别。正常的 EGF 由于不考虑外侧的排列,为 fi(x)=j=0∑aij!xj。而当选定连续子序列中数字个数确定,外侧也随之确定,他们的排列数为 (ai−j)!(n−s+1)!,因而可以将此系数杂糅进 fi(x)。
考虑使用分治 NTT 去计算上式:对于区间 (l,r) 维护 F1(x,l,r)=i=l∏rgi(x),F2(x,l,r)=i=l∏rfi(x),F3(x,l,r)=i=l∑r−1j=l∏igi(x)j=i+1∏rfi(x),则有 F3(x,l,r)=F1(x,l,m)F3(x,m+1,r)+F3(x,l,m)F2(x,m+1,r)−F1(x,l,m)F2(x,m+1,r)。因而可以 O(nlog2n) 的通过。
Poly f[N + 5], g[N + 5], gf[N + 5];
int a[N + 5];
void build(int place, int left, int right)
{
if (left == right)
{
f[place].resize(a[left] + 1);
for (int i = 0; i <= a[left]; i++)
f[place][i] = 1ll * ifac[i] * ifac[a[left] - i] % P;
g[place] = f[place];
g[place][0] = 0;
gf[place] = f[place] + g[place];
return;
}
int mid = (left + right) >> 1;
build(place << 1, left, mid);
build(place << 1 | 1, mid + 1, right);
f[place] = f[place << 1] * f[place << 1 | 1];
g[place] = g[place << 1] * g[place << 1 | 1];
gf[place] = g[place << 1] * gf[place << 1 | 1] + gf[place << 1] * f[place << 1 | 1] - g[place << 1] * f[place << 1 | 1];
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i <= n;i++)
scanf("%d", &a[i]);
build(1, 0, n);
Poly F = gf[1] - f[1];
int ans = 0;
for (int i = 1; i <= n; i++)
ans = (ans + 1ll * (n - i + 1) * fac[i] % P * fac[n - i] % P * F[i]) % P;
printf("%d", ans);
return 0;
}