Codeforces 1707D. Partial Virtual Trees (3000)
给定一棵以 \(1\) 为根的有根树,有一个集合 \(S=\{1,2,\dots,n\}\)。可以对这个集合进行如下操作:
选择一个集合 \(T\),满足 \(T\subsetneq S\),且 \(\forall x\neq y\in T,\text{lca}(x,y)\in T\)。令 \(S\gets T\)。
对于 \(k=1\sim n-1\) 求出有多少种方式使得恰好操作 \(k\) 次之后,\(S=\{1\}\),答案对给定模数 \(p\) 取模。
\(1\leq n\leq 2000,10^8+7\le p\le 10^9+9\)。
首先考虑将 \(S_1\neq S_2\) 的限制去掉,若去掉后求得的答案为 \(f_n\),原答案为 \(ans_n\),则有
即考虑其中 \(i\) 次 \(S_1\neq S_2\)。移项则有
那么问题转化成了求解 \(f_n\),题目中的条件也就是保持 \(S_1\) 中的点组成一个连通块,那么节点 \(x\) 可被删当且仅当 \(x\) 的所有子节点为根的子树都被删光或者除了以 \(x\) 某个子节点 \(v\) 为根的子树没被删光,其余整个树所有节点都被删光。
设 \(dp_{x,t}\) 表示以 \(x\) 为根的子树在 \(t\) 时刻恰好被删光的方案数。则有
分别为考虑 \(x\) 在时刻 \(t\) 被删光以及以 \(x\) 的某个子节点 \(v\) 为根的子树最后被删光的方案数。
记 \(s_{x,t}=\sum\limits_{p\le t}dp_{x,p},g_{x,t}=\prod\limits_{v\in son_x}s_{v,t}\),然后使用树形 \(\text{dp}\) 解决,总时间复杂度 \(O(n^2)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
int n,mod;ll fac[N],inv[N],Inv[N],ans[N],pmul[N][N],smul[N][N],s[N][N],dp[N][N];
vector<int>G[N];
inline ll qpow(ll a,ll b){
ll ans=1;
for(;b;b>>=1,a=a*a%mod)if(b&1)ans=ans*a%mod;
return ans;
}
inline void precalc(int n){
fac[0]=inv[0]=Inv[0]=fac[1]=inv[1]=Inv[1]=1;
for(int i=2;i<=n;++i)
fac[i]=fac[i-1]*i%mod,
Inv[i]=(mod-mod/i)*Inv[mod%i]%mod,
inv[i]=inv[i-1]*Inv[i]%mod;
}
inline ll C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline void dfs(int x,int fa){
vector<int>son;
for(auto y:G[x])if(y^fa)dfs(y,x),son.emplace_back(y);
if(son.empty()){
for(int i=1;i<=n;++i)dp[x][i]=1,s[x][i]=i;
return;
}
for(int t=1;t<=n;++t){
pmul[0][t]=s[son[0]][t],smul[son.size()][t]=1;
for(int id=1;id<son.size();++id)
pmul[id][t]=pmul[id-1][t]*s[son[id]][t]%mod;
for(int id=(int)son.size()-1;~id;--id)
smul[id][t]=smul[id+1][t]*s[son[id]][t]%mod;
}
for(int t=1;t<=n;++t)dp[x][t]=smul[0][t];
if(x^1)for(int v=0;v<son.size();++v){
ll S=0;
for(int t=1;t<=n;++t){
(dp[x][t]+=dp[son[v]][t]*S)%=mod;
(S+=(v>0?pmul[v-1][t]:1)*smul[v+1][t])%=mod;
}
}
for(int i=0;i<=son.size();++i)
for(int t=0;t<=n;++t)
pmul[i][t]=smul[i][t]=1;
for(int t=1;t<=n;++t)(s[x][t]=s[x][t-1]+dp[x][t])%=mod;
}
int main(){
scanf("%d%d",&n,&mod),precalc(N-1);
for(int i=1,x,y;i<n;++i)scanf("%d%d",&x,&y),G[x].emplace_back(y),G[y].emplace_back(x);
dfs(1,0);
for(int t=1;t<n;++t){
ans[t]=dp[1][t];
for(int i=0;i<t;++i)(ans[t]+=mod-C(t,i)*ans[i]%mod)%=mod;
printf("%lld ",ans[t]);
}
return 0;
}