题目链接:https://codeforces.com/gym/104076/problem/C
简要题意:给定一棵n个点的有根树,对于所有的二元组
(
i
,
j
)
(i,j)
(i,j)求这颗树所有可能的dfs序中有多少个dfs序满足第
i
i
i个点出现在dfs序第
j
j
j个位置。
赛场上假了无数次以后,我终于才理清楚了这题的dp思路。
状态定义:
定义
d
p
[
u
]
[
i
]
dp[u][i]
dp[u][i]表示只考虑
u
u
u子树外的点的情况下,dfs序中在
u
u
u前面有
i
i
i个点的方案数。注意,这个
d
p
dp
dp值并不能直接作为答案,还要乘上
u
u
u子树内部的所有可能的dfs序方案数。显然这个
d
p
dp
dp的取值与
u
u
u子树的情况无关,因此这道题
d
p
dp
dp的转移与一般树形
d
p
dp
dp不同,这道题应当自上而下用父亲的信息更新儿子的信息。上文提到过,为了得到答案,我们还需要
u
u
u子树内部的dfs序方案数量,因此定义
d
p
2
[
u
]
dp2[u]
dp2[u]表示
u
u
u子树内的dfs序方案数。
状态转移:
设我们当前在
u
u
u点,我们考虑更新
u
u
u的一个儿子
v
v
v的
d
p
dp
dp信息,我们需要知道dfs序有多少个点在
v
v
v前面,我们把这些点分为在
u
u
u的子树内和
u
u
u的子树外两类,最后类似背包的思路合并即可。
而对于dp2数组,我们需要在处理dp数组前就提前dfs一次先得到它。设
u
u
u总共有
c
n
t
cnt
cnt个儿子,考虑
u
u
u子树内所有可能的dfs序,转移显然:
d
p
2
[
u
]
=
c
n
t
!
∗
∏
d
p
2
[
v
]
dp2[u]=cnt!*\prod dp2[v]
dp2[u]=cnt!∗∏dp2[v]
此时,第
i
i
i个点出现在dfs序中第
j
j
j个位置的方案数就是
d
p
[
i
]
[
j
−
1
]
∗
d
p
2
[
i
]
dp[i][j-1]*dp2[i]
dp[i][j−1]∗dp2[i]。
参考代码:(比赛时写的代码)
#include
#define ll long long
using namespace std;
const int N=505,mod=998244353;
int n,dp[N][N],siz[N],c[N][N],d[N][N],b[N],fac[N],dp2[N];
vector<int>g[N];
void Add(int&x,int y){
((x+=y)>=mod)&&(x-=mod);
}
inline int mul(int x,int y){
return 1ll*x*y%mod;
}
inline int dec(int x,int y){
return x<y?x-y+mod:x-y;
}
inline int ksm(int a,int b){
int ret=1;
while(b){
if(b&1)ret=mul(ret,a);
a=mul(a,a);
b>>=1;
}return ret;
}
void dfs1(int u,int fa){
siz[u]=1;
dp2[u]=1;
int cnt=0;
for(int v:g[u]){
if(v==fa)continue;
cnt++;
dfs1(v,u);
siz[u]+=siz[v];
dp2[u]=mul(dp2[u],dp2[v]);
}
dp2[u]=mul(dp2[u],fac[cnt]);
}
void dfs2(int u,int fa){
int cnt=0,sum=1;
memset(c,0,sizeof c);
c[0][1]=1;
int lsr=1;
for(int v:g[u]){
if(v==fa)continue;
cnt++;sum+=siz[v];
lsr=mul(lsr,dp2[v]);
for(int i=cnt;i>=1;i--)
for(int j=sum;j>=siz[v];j--)
Add(c[i][j],c[i-1][j-siz[v]]);
}
for(int v:g[u]){
if(v==fa)continue;
memset(d,0,sizeof d);
for(int i=0;i<=cnt;i++)
for(int j=0;j<=siz[u];j++)
d[i][j]=dec(c[i][j],(j>=siz[v]&&i>0)?d[i-1][j-siz[v]]:0);
memset(b,0,sizeof b);
for(int i=0;i<cnt;i++)
for(int j=0;j<=siz[u];j++)
Add(b[j],mul(mul(fac[i],fac[cnt-i-1]),d[i][j]));
int bas=mul(lsr,ksm(dp2[v],mod-2));
for(int i=0;i<=n;i++)
for(int j=0;j<=siz[u];j++)
Add(dp[v][i+j],mul(bas,mul(dp[u][i],b[j])));
}
for(int v:g[u]){
if(v!=fa)dfs2(v,u);
}
}
int main()
{
scanf("%d",&n);
fac[0]=1;
for(int i=1;i<=n;i++)fac[i]=mul(fac[i-1],i);
for(int i=2;i<=n;i++){
int u,v;
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dp[1][0]=1;
dfs1(1,0);
dfs2(1,0);
for(int i=1;i<=n;i++){
for(int j=0;j<n;j++)
cout<<mul(dp[i][j],dp2[i])<<" ";
cout<<"\n";
}
}