题解[HEOI2013]SAO.md
题意
给定一棵树,边有方向。要给点重标号,使得边都由小编号指向大编号,求重标号方案数。
思路
有关树的计数。套路性考虑树形 DP。
设计状态 表示 的子树的合法重标号方案数。如何转移?发现不好转移。
考虑什么情况下可以转移呢?发现转移是合并当前 子树中已经考虑了部分的状态和其某还未考虑的儿子 的子树的状态,而转移合法只要保证 和 在重标号中相对大小关系符合边上的限制。那么我们考虑改状态为 表示 的子树重标号且 的新编号为 的方案数。如何转移?
大于和小于的情况是对称的,所以我们只讨论 的新编号要小于 的情况,另一种情况的转移类似。我们考虑状态 能转移到什么状态。考虑在新的重标号方案中,在 的子树中拿 个点标上小于 的编号, 标上编号 ,那么考虑转移方程中需要哪些系数呢?首先,考虑 能与哪些 合并得到新的状态,发现 子树中前新编号前 小的点都拥有小于 的新编号,而 的新编号不能小于 ,所以要保证 ,也就说这次转移中合法的 的子树重编号方案数是 。其他系数?要在 个新编号小于 的点中选 个点来自 的子树,这里需要乘上 。新编号大于 的同理,乘上 。总的式子就是 。发现前缀和可以优化掉那个 。统计答案就是 。
计算一下复杂度,看似是 ?每次合并复杂度为 ,这与树形背包是一样的复杂度,所以总复杂度应该是 。可以理解为 子树中已考虑的每个点和 子树中每个点都 计算一次,发现每对点都只会在其 LCA 处计算一次,所以总复杂度是点对数即 。
实现
void DP(int u,int fa){
sz[u]=1;
for(int i=1;i<=n;++i)
f[u][i]=0;
f[u][1]=1;
for(int ie=head[u],v=e[ie].t;ie;ie=e[ie].n,v=e[ie].t)
if(v!=fa){
DP(v,u);
for(int i=1;i<=sz[u]+sz[v];++i)
tmp[i]=0;
if(e[ie].op)
for(int i=1;i<=sz[u];++i)
for(int j=1;j<=sz[v];++j)
MAdd(tmp[i+j-1],Mul(Mul(Add(g[v][sz[v]],MOD-g[v][j-1]),f[u][i]),Mul(C(i+j-2,j-1),C(sz[u]-i+sz[v]-j+1,sz[v]-j+1))));
else
for(int i=1;i<=sz[u];++i)
for(int j=1;j<=sz[v];++j)
MAdd(tmp[i+j],Mul(Mul(g[v][j],f[u][i]),Mul(C(i+j-1,j),C(sz[u]-i+sz[v]-j,sz[v]-j))));
sz[u]+=sz[v];
for(int i=1;i<=sz[u];++i)
f[u][i]=tmp[i];
}
for(int i=1;i<=sz[u];++i)
g[u][i]=Add(g[u][i-1],f[u][i]);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」