题解:CF1806D DSU Master
DSU:并查集。
拆贡献,考虑每个点对 1 号点入度的贡献。
如果一个点 \(i\) 想直接接在 1 号点上,则是通过执行 \(i-1\) 做到的,且 \(1\thicksim i-1\) 的点一定接到 \(1\) 的连通块里面了,而其他的点对此过程没有影响,最后乘上排列数即可。
记 \(f_i\) 为 \(i\) 连到 1 上时,\(1\thicksim i-1\) 都已经被执行的方案数。
枚举 \(i\) 之前产生贡献的 \(j\),则 \(j<i\) 且 \(1 \thicksim j-1\) 都出现了,记为 \(f_j\)。多出的 \(j+1\thicksim i-1\) 对 \(f_j\) 也没影响,变成排列数(\(i-1\) 次操作选 \(i-j-1\) 次)。最后,为了把 \(i\) 合并到 1,\(a_{i-1}=0\)。
所以:
\[f_i=[a_{i-1}=0]\sum_{j=1}^{i-1} A_{i-2}^{i-j-1}f_j
\]
方便递推做一步转化,\(a_{i-1}=0\) 时不把它设为 0:
\[f_i=[a_{i-1}=0]\sum_{j=1}^{i-1} \frac{(i-2)!}{(j-1)!}f_j
\]
即:
\[f_i=f_{i-1}\times (i-2) +[a_{i-1}=0]f_{i-1}=f_{i-1}\times (i-1-a_{i-1})
\]
\(f_1=1\)。
再加上前面说的排列数(\(m-1\) 次操作选剩下的):
\[ans_m=\sum_{i=1}^{m-1} A_{m-1}^{m-1-i}f_{i-1}=\sum_{i=1}^{m-1} \frac{(m-1)!}{i!}f_{i-1}
\]
同时发现:
\[ans_m=ans_{m-1}\times (m-1)+[a_{i-1}=0]f_{m-1}
\]
则时间复杂度为 \(O(n)\)。
#include <bits/stdc++.h>
using namespace std;
#define gc() (rp1==rp2&&(rp2=(rp1=buf)+fread(buf,1,1<<22,stdin))==rp1?EOF:*rp1++)
char buf[1<<22],*rp1,*rp2;
inline int read(){
int d=0,f=0;char ch=gc();
while (!isdigit(ch)) f|=(ch=='-'),ch=gc();
while (isdigit(ch)) d=d*10+ch-'0',ch=gc();
return f?-d:d;
}
const int N=500005,mod=998244353;
int n,a[N],f[N],ans[N];
int main(){
int T=read();
while (T--){
n=read();f[1]=1;
for (int i=1;i<n;i++) a[i]=read();
for (int i=2;i<=n;i++) f[i]=1ll*f[i-1]*(i-1-a[i-1])%mod;
ans[1]=0;
for (int i=2;i<=n;i++){
ans[i]=(1ll*ans[i-1]*(i-1)+(!a[i-1])*f[i-1])%mod;
printf("%d ",ans[i]);
}
puts("");
}
return 0;
}

浙公网安备 33010602011771号