P8867 [NOIP2022] 建造军营

这道题想了很久,终于想出来了,非常抽象。

经过一番无脑推导,我们发现u里面有没有军营,是否与根连通,u的子树有没有军营,……都对方案数有影响,然后我就一直修修改改,事实证明,当发现越来越多题目条件中被忽略的细节时,一定不要嫌麻烦,要从头开始设置状态。

首先我们发现,子树中有没有军营对于与子树相连的边选不选是有影响的,也就是说对状态多了一个限制:有没有军营。

然后就会想:子树如果没有军营,儿子边可选可不选;有军营,就一定要选……等等,一定要选吗?如果只选了一个儿子,而且u没有军营的话,其实是可以不选的,然后我尝试加加减减乘乘除除。然后你会发现,子树有军营,但是没和v连在一起,就算儿子边选了,u和军营还是不连通,……根本算不出来……于是整理一下思路。

我们注意到,dp难以转移的关键在于子树中军营与u的连通性(只选一个儿子,可以不连通,选了多个儿子,又一定要连通)。

  1. 如果子树中恰好只有一个儿子v,T(v)中有军营,那么它与u是否连通是无所谓的。

  2. 如果子树中有超过两个儿子v,T(v)中都有军营,那么它们就一定要与u连通(也就一定要与v连通)。

考虑T(v),此时多了一个限制条件,即“T(v)中的军营与v是否连通”。

f(u,0)表示没有军营的方案数。
f(u,1)表示有军营,并且军营与u连通的方案数。
f(u,2)表示有军营,并且军营与u不连通的方案数。

由于每次分类是不重不漏的,所以答案就是有军营的方案,也就是有军营、连通或有军营、不连通的和,即f(u,1)+f(u,2)。

考虑转移(记u到儿子的边为儿子边):

f(u,0):因为T(u)都没有军营,(其实可以用公式算,但是我懒),所以每个儿子都一定不选,而且儿子边选也可以,不选也可以,所以就是每个2*f(v,0)乘起来,最后乘上u自身的方案(没有军营,方案等于点的方案乘上边的方案,也就是1×2cnte(u))。

f(u,1):先考虑单个儿子v,如果T(v)里面有军营(注意,不是v里面有军营,这也是之前推错的原因),那么它一定要和u连通,也就是边一定选:1*f(v,1);如果T(v)没有军营,那么边可选可不选,也就是2f(v,0),注意到这里是分类,所以用加法原理,每个子树的方案就是 f(v,1)+2f(v,0)

自然地想到,每个儿子用乘法原理乘起来就行了,最后再乘上u的方案即可。

这里重点来了:每次写完转移之后,都要考虑这样转移是否充要。上述写法就是说:所有儿子,要么选并且连通,要么不选的方案数。而这样的集合里面有没有军营的方案数,而状态定义中是要求一定要有军营的,所以要减去多出来的方案。

我们会发现,全部不选,也就是u不选点,儿子也不选点,那么也就是T(u)没有军营,那不就是f(u,0)么?

所以减去f(u,0)就行了。

f(u,2):同样,考虑单个儿子v,……

然后你就会发现方案数统计多了,这时候把所有方案画出来,一一与题设比对,找出不合法的方案,并将其剔除。

于是我们发现,如果军营不与u连通,那么只能有一个子树有军营。(不然两个军营之间没有保护的边,不符合题意)。

所以我们枚举这个有军营的儿子,其它儿子都没有军营,儿子边自然是可选可不选。因为这个有军营的儿子与u不相连,考虑儿子边的选法:选,则v和u相连了,军营不能和v连通,所以是f(v,2);不选,则v和u不相连,那么军营和v连不连通就无所谓了,所以是f(v,2)+f(v,1)。合起来就是2f(v,2)+f(v,1)。其它儿子都不能选,那么用一个累乘和逆元搞一下就行了。

上述选法会统计恰好一个子树有军营,且与u不连通的方案数,符合状态设计。

然后就拿下啦!

CODE

总结一下:

  1. 推导的时候,肯定会逐渐发现一些没有注意到的题目细节要求,当细节越来越多,状态会越来越乱,此时要重新定义状态。
  2. 列出所有发现的要求,列出来,抽象概括出若干个能够符合所有要求的根本要求,比如本题的根连通性和是否有军营。
  3. 然后根据每一个要求,依次分类讨论,其实本来是要有4类的,但是因为没有军营就不需要考虑连通性了(都没有点哪来连通性问题),所以本题只有3个状态。
  4. 推出式子之后记得返回去检查这样统计出来的方案数是否充要。
  5. 树形DP,可以先考虑每一个儿子,然后推出一个必要条件的方案数(每一个儿子都必须这样),然后再剔除不合法的方案(其实几乎所有情况都是状态定义中有要求至少XXX,但是求出来的情况包括了全部都没有的情况)。

update: 2023/9/23

昨天的想法,停留在对于一个集合,用闫氏DP分析法划分集合,最后通过组合计数求得集合方案数,这种方法在情况过于复杂时难以正确求解。

这篇题解 中的思路本质上和闫氏DP分析法不一样,所以昨天没看懂。

文中DP转移的核心逻辑是:假定当前已经遍历完了若干个子树(可能没有),并且此时f(u,0)和f(u,1)的值都是正确的,那么加入新的子树v之后,f(u)应该变为什么。

考虑每新增一个子节点v对f(u,1)产生的贡献

如果每一步都是正确的,那么最后,也就是考虑到了整颗树T(u)时,自然是正确的了。

可以理解为多设置了一个状态f(u,1,i),表示考虑u的前i个儿子时的方案数,然后每次多一个儿子进行转移,只是最后一维没有显示地写出来而已。

这种思路每次只需要考虑一棵子树的贡献,相比之下,本题使用闫氏DP分析法,就要同时考虑所有子树的贡献,显然前者思路上更为清晰,可行性更强。

他最后统计答案时,使用的是闫氏DP分析法:我们考虑将所有可行方案分类,根据状态表示“有军营,且与u连通”的方案数,及常见的根据最值分类的原则。这里的方案又是一棵树上的,与u连通,意味着军营通过保卫的边能到达的深度最小的点是u。

上述是一个感性推导,有了思路之后,来严谨分类。

将所有方案按照其中军营能通过保卫的边到达的深度最小的点u分类。

注:根据题意,任意两个军营必然有至少一条只通过保卫边的路径可达,否则他们就会因为B国的袭击而分开。

T(u)中的方案,根据定义,就是f(u, 1);接下来考虑T(u)外的方案。

  • 考虑点

T(u)外深度不小于u的点(形象理解为兄弟节点及其子树),如果里面有军营,比如边双连通分量v,那么根据题意,v和u就必然有一条路径相连,而且因为是在树上,那么这条路径必然经过它们的最近公共祖先,而公共祖先的深度一定小于u(u和v是兄弟节点),与u是深度最小的点矛盾,所以不可能选。

如果有T(u)外深度小于u的点选了,那么就与u是能抵达的深度最小的点矛盾,所以不可能。

综上,T(u)外的点肯定不选,方案数是1。

  • 考虑边

不难发现,因为点都一定不选,所以军营能到达的点全部由u到其父亲的边所决定,也就是u到fa的边一定不能选(不然矛盾,fa深度更小),其他边随便选。

也就是 2mE(u)1

需要注意,根节点1是没有父节点的,所以不需要减1,此时方案数是 2mm=1

最后将T(u)内、外的方案数乘起来,就是答案了。

posted @   Zlc晨鑫  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示