标签:ace struct ++ ems mat c++ += memset 合并
题目链接:https://www.ybtoj.com.cn/contest/62/problem/3
可以看做有多少个 \(1\sim n\) 的排列满足对于一条路径 \(u\to v\),\(u\) 在序列中的位置一定在 \(v\) 在序列中的位置的前面。
设 \(f[x][i]\) 表示以 \(x\) 为根的子树内,\(x\) 在序列中是第 \(i\) 个数的方案数。
考虑加入 \(x\) 的一个子树 \(y\),枚举在子树 \(y\) 内有多少个数字排在 \(x\) 前面,那么最终合并之后点 \(x\) 是排在位置 \(i+j\) 的。
而前面的 \((i-1)+j\) 个数字可以任意排列,后面同理,所以方案数为
其中 \(s\) 是 \(y\) 内的方案数,具体的,当 \(p_x<p_y\) 时,\(s=\Pi^{\mathrm{size}(y)}_{k=j+1}f[y][k]\),否则 \(s=\Pi^{j}_{k=1}f[y][k]\)。
由于我们只会枚举到 \(\mathrm{size}(y)\),所以两个点只会在他们 LCA 处计算,时间复杂度 \(O(n^2)\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=3010,MOD=998244353;
ll n,tot,f[N][N],g[N],head[N],size[N],C[N][N];
ll ans;
struct edge
{
ll next,to,id;
}e[N*2];
void add(ll from,ll to,ll id)
{
e[++tot].to=to;
e[tot].id=id;
e[tot].next=head[from];
head[from]=tot;
}
void dfs(ll x,ll fa)
{
size[x]=1; f[x][1]=1;
for (ll i=head[x];~i;i=e[i].next)
{
ll v=e[i].to;
if (v!=fa)
{
dfs(v,x);
size[x]+=size[v];
for (ll j=0;j<=size[x];j++)
g[j]=f[x][j],f[x][j]=0;
ll sum=0;
if (e[i].id)
for (ll j=1;j<=size[x]-size[v];j++,sum=0)
for (ll k=size[v];k>=0;k--)
{
f[x][j+k]=(f[x][j+k]+1LL*C[j+k-1][j-1]*C[size[x]-j-k][size[v]-k]%MOD*g[j]%MOD*sum)%MOD;
sum=(sum+f[v][k])%MOD;
}
else
for (ll j=1;j<=size[x]-size[v];j++,sum=0)
for (ll k=0;k<=size[v];k++)
{
sum=(sum+f[v][k])%MOD;
f[x][j+k]=(f[x][j+k]+1LL*C[j+k-1][j-1]*C[size[x]-j-k][size[v]-k]%MOD*g[j]%MOD*sum)%MOD;
}
}
}
}
int main()
{
freopen("perm.in","r",stdin);
freopen("perm.out","w",stdout);
memset(head,-1,sizeof(head));
C[0][0]=C[1][0]=C[1][1]=1;
for (ll i=2;i<=3000;i++)
{
C[i][0]=1;
for (ll j=1;j<=i;j++)
C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
}
scanf("%lld",&n);
for (ll i=1,x,y;i<n;i++)
{
scanf("%lld%lld",&x,&y);
add(x,y,1); add(y,x,0);
}
dfs(1,0);
for (ll i=1;i<=n;i++)
ans=(ans+f[1][i])%MOD;
printf("%lld",ans);
return 0;
}
标签:ace struct ++ ems mat c++ += memset 合并
原文地址:https://www.cnblogs.com/stoorz/p/14032320.html