TC SRM673 DIV2 T3 BearPermutations2

本文探讨了一类涉及笛卡尔树及其全排列的问题,通过树型动态规划的方法找到了高效的解决方案。文章详细分析了笛卡尔树的特性,并给出了具体的DP状态转移方程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

最近打TC,做到DP的题很多,以树型DP为主。觉得这些题都挺好的,解出来也是用了不少时间。因此,决定写几篇题解在巩固一下。

题意

给出两个数NNMOD,对于NN的所有全排列,对于每个全排列建笛卡尔树T,定义TT的分值为其所有有两个子节点的节点两子节点在原排列中索引的差值(非负值)之和。求这个所有T的分值之和XXMOD取模后的值。

约定

1N1001≤N≤100
3MOD109,MODwillbeprime3≤MOD≤109,MODwillbeprime

分析

乍一看题,并没有什么思路马上冒出来。这时候,去蛮力地分析是没有效果的。我们需要从笛卡尔树的本质去分析。

PART 1 笛卡尔树的本质

注意到笛卡尔树一棵子树对应的是原序列中一段连续的区间,所以对于一棵给定的子树,这个差值大小只会与区间内元素相对的大小关系有关,并不需要考虑它们具体是什么值。也就是说,就像物理中的相对位置,差值的大小和具体的数值并没有什么关系。

PART 2 动态规划

事实上,上面我们找到的性质对后面的推理有极大的帮助。
我们令DP[N]DP[N]NN的全排列的分值之和,考虑1所在的位置ii,它会将原排列一分为二,在树上就是根节点一左一右一棵更小的笛卡尔树。我们可以分左右两部分来计算左右两棵子树的贡献。
首先,若i在第一个或者最后一个位置,那么贡献就是DP[i1]DP[i−1]
ii在中间位置时,我们需要先需要选i1个元素放在11的左边,剩余的放在1的右边。这样子的方案数就是(N1i1)(N−1i−1)
接下来我们考虑每种情况的贡献。
首先对于左子树,对于右边的所有可能排列,它都会产生自己的一个贡献,即(ni)!DP[i1](n−i)!∗DP[i−1]
然后,考虑两颗子树根节点新产生的贡献,我们设左子树根节点的数值为xx,右子树根节点数值为y,那么这个贡献就是yxy−x,再进一步,变为yi+ixy−i+i−x,这样就可以分开计算这个贡献。
而对于右边区间的所有排列,左子树根节点的值可以随意选,并且,对于每个确定了的左子树根节点,其他i-2个树又可以任意排列,那么贡献就是(ni)!(i1+...+2+1)(i2)!=(ni)!i(i1)÷2(i2)!=(ni)!i!÷2(n−i)!∗(i−1+...+2+1)∗(i−2)!=(n−i)!∗i∗(i−1)÷2∗(i−2)!=(n−i)!∗i!÷2
整合一下式子,并加上右子树的贡献,可以得到转移方程:

DP[N]=i=1N(N1i1)((ni)!(DP[i1]+i!÷2)+(i1)!(DP[Ni]+(N+1i)!÷2))DP[N]=∑i=1N(N−1i−1)((n−i)!∗(DP[i−1]+i!÷2)+(i−1)!∗(DP[N−i]+(N+1−i)!÷2))

当然上面的方程需要对i=1i=1i=Ni=N特别处理。
最后注意要取模,所以不能直接进行除法,应该乘上2的逆元。(一开始忘记了各种WA然后看不出来)

参考代码

using namespace std;
typedef long long LL;
const int MAXN = 105;

class BearPermutations2 {
public:s
    int getSum( int N, int MOD );
private:
    int Mod;
    void plus(int & x, int d) { x = (x + d) % Mod; }
    LL pow2(int ex) {
        LL res = 1, pw = 2;
        while (ex) { if (ex & 1) res = res * pw % Mod; pw = pw * pw % Mod; ex >>= 1; }
        return res;
    }
};

int C[MAXN][MAXN], Fac[MAXN], F[MAXN];

int BearPermutations2::getSum(int N, int MOD) {
    int i, j; Mod = MOD;
    for (C[0][0] = Fac[0] = i = 1; i <= N; i++)
        for (Fac[i] = (LL)i * Fac[i - 1] % MOD, C[i][0] = j = 1; j <= i; j++) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
    memset(F, 0, sizeof(F));
    LL inv2 = pow2(MOD - 2);    // 阶乘求逆元
    for (i = 3; i <= N; i++)
        for (F[i] = (F[i - 1] << 1) % MOD, j = 2; j < i; j++)
            plus(F[i], ((F[j - 1] + Fac[j] * inv2 % MOD) % MOD * Fac[i - j] % MOD + 
                        (F[i - j] + Fac[i - j + 1] * inv2 % MOD) % MOD * Fac[j - 1] % MOD) % MOD * C[i - 1][j - 1] % MOD);
    return F[N];
}

总结

这道题思维上非常具有挑战性,如果从错误的方向思考可能很久也找不出方法。因此,对于这类给定一个概念的题目,如果一般的做法下手困难,一定要从概念的定义出发,寻找一些重要的性质,从而一击解出答案。

内容概要:文章基于4A架构(业务架构、应用架构、数据架构、技术架构),对SAP的成本中心和利润中心进行了详细对比分析。业务架构上,成本中心是成本控制的责任单元,负责成本归集与控制,而利润中心是利润创造的独立实体,负责收入、成本和利润的核算。应用架构方面,两者都依托于SAP的CO模块,但功能有所区分,如成本中心侧重于成本要素归集和预算管理,利润中心则关注内部交易核算和获利能力分析。数据架构中,成本中心与利润中心存在多对一的关系,交易数据通过成本归集、分摊和利润计算流程联动。技术架构依赖SAP S/4HANA的内存计算和ABAP技术,支持实时核算与跨系统集成。总结来看,成本中心和利润中心在4A架构下相互关联,共同为企业提供精细化管理和决策支持。 适合人群:从事企业财务管理、成本控制或利润核算的专业人员,以及对SAP系统有一定了解的企业信息化管理人员。 使用场景及目标:①帮助企业理解成本中心和利润中心在4A架构下的运作机制;②指导企业在实施SAP系统时合理配置成本中心和利润中心,优化业务流程;③提升企业对成本和利润的精细化管理水平,支持业务决策。 其他说明:文章不仅阐述了理论概念,还提供了具体的应用场景和技术实现方式,有助于读者全面理解并应用于实际工作中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值