题解: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;
}
posted @ 2026-03-27 17:16  TP2010  阅读(6)  评论(0)    收藏  举报