P6383 - Resurrection
每删一条边 \((x,y)\) 其中 \(x\) 是 father,删去它之后 \(y\) 显然是所在连通块最大的,\(x\) 所在连通块的话应该选 \(x\) 最浅的能到达的(中间边没被删的)点。这样子如果连有向边的话不难发现每个点都恰有一条出边,而且不难证明构成一棵内向树。于是只要对 father 序列 DP 即可。
那就需要考虑怎样的 father 序列至少能找到一个合法删边顺序。注意到对每个 \(x\neq n\) 以及它的 father \(y\),在原树上 \(x\to y\) 这条链(不包括 \(x,y\))必须在 \(x\) 删边之后删边,\(y\) 要在 \(x\) 之前删边,显然这样构出来一张图无环是充要条件。考虑找一找无环的更可做的充要条件。
首先把新内向树反过来得到外向树显然是一个子图,\(x\to\) 链上点的边就不是树边。首先有一个对一步头造成环的情况的 observation:对原树上 \(x,y\) 其中 \(x\) 是 \(y\) 祖先,\(fa_x\) 不能是 \(fa_y\) 的真祖先。如果是的话那显然会在新树上构成一个由一条树边两条非树边构成的三元环。然后我们能证明只排除这种情况也是充分的。。。考虑模拟拓扑排序:任意时刻被删除的点显然是一个包含 \(n\) 的连通块,只需要证明这个连通块的儿子们里存在某个点不被没删除的点通过一条边到达。如果选编号最小的点 \(x\) 那显然不被连通块的儿子们到达了(因非树边一定是原树上后代到祖先),那下面的其它点呢?假设存在这样一个点 \(y\) 使得 \(x\in y\to fa_y\),那么跟据排除掉的情况只能 \(x\to fa_x\subsetneq y\to fa_y\),然后 \(fa_x\) 再往上连肯定逃不出去,最后的结果就是在新树上一定是 \(y\) 是 \(x\) 的祖先,而此时 \(x\) 的祖先显然都被删去了,矛盾,得证。
然后就可以 DP 了。先考虑某个子树,对根的 father 在它的祖先里选,进行决策。选了之后,根的儿子们的 father 就只能选根的决策的假祖先或者根本身了。再考虑将这个状态(只能选父亲或祖先序列的一个前缀)放到 DP 状态里,一直迭代下去,发现状态封闭不了,任何祖先序列的子序列都能当状态,复杂度爆裂。但是我们发现只有能选的祖先数量才重要,永远都是选了第 \(x\) 个祖先那么儿子们可以选长度为 \(x\) 的祖先前缀以及 \(x\) 本身。那么设 \(dp_{i,j}\) 表示子树 \(i\) 中 \(i\) 有 \(j\) 个祖先可选作 father,转移显然是 \(dp_{i,j}=\sum\limits_{k=2}^{j+1}\prod\limits_{son}dp_{son,k}\),暴力显然三方,递推优化显然可平方。
code
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=3010;
const int mod=998244353;
int n;
vector<int> nei[N];
int dp[N][N];
void dfs(int x=n,int fa=0){
vector<int> tmp(n+1,1);
for(int i=0;i<nei[x].size();i++){
int y=nei[x][i];
if(y==fa)continue;
dfs(y,x);
for(int j=1;j<=n;j++)tmp[j]=1ll*tmp[j]*dp[y][j]%mod;
}
for(int i=1;i<=n;i++)dp[x][i]=(dp[x][i-1]+tmp[i+1])%mod;
}
int main(){
cin>>n;
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
nei[x].pb(y),nei[y].pb(x);
}
dfs();
int ans=1;
for(int i=0;i<nei[n].size();i++)ans=1ll*ans*dp[nei[n][i]][1]%mod;
cout<<ans;
return 0;
}