Codeforces 1111 E. Tree(虚树,DP)

题意


有一棵树,q个询问,每次询问,指定一个点做树根,再给定k个点,要求把这些点分成不超过m组的方案数,分配的限制是任意两个有祖先关系的点不能分在同一组。题目还保证了所有的询问的k加起来不超过1e5。

思路


如果直接在原树上DP计数,那么q次询问下的DP总复杂度是平方级别的,显然不对。
由于询问点数的总和很少,所以考虑在虚树上计数。(不了解虚树的可以先学习一下,大概思想是根据询问的点来重新建一颗包含关键信息,但是规模较小的树)于是多次询问的问题就解决了。
难点转到考虑虚树上的dp计数。我们按照dfs序来dp,定义f[i][j]表示遍历到的前i个点分成j组的方案数,cnt[i]表示点i到根有多少个询问点。
那么对于f[i][j]:
①如果j<=cnt[i],也就说明光是把i的祖先们分开就需要用到j个组了,那么此时点i必然无组可放,也就是说此时没有方案行得通,于是f[i][j]=0;
②否则,f[i][j]=f[i-1][j-1]+f[i-1][j]*(j-cnt[i]).这个应该不难理解,我们是按照dfs序来dp的,dp到点i时其祖先一定已经更新完了。那么对于新来的点i,它要么新开一组,方案数为f[i-1][j-1],要么加入到已有的组中,方案数为f[i-1][j]*(j-cnt[i]),(即从已有的组中扣去那些祖先所在的组)。
考虑到空间有点吃紧,可以滚动dp,当然精打细算一点,开二维数组来DP的空间也正好够。

代码

#include
#define dd(x) cout<<#x<<" = "<

你可能感兴趣的:(Codeforces 1111 E. Tree(虚树,DP))