A
容易发现,对于一条边(x,y),如果不存在w(x,y)=w(x,z)+w(z,y),那么(x,y)这条边必须保留,否则x到y可以通过从x先走到z再走到y到达。于是我们只要找到所有需要保留的边。
正确性按边权从小到大考虑即可。
B
按题意模拟即可,首先对于一个集合和这个集合的取值,判断是否为certificate。然后对于每个输入求出最小的certificate。
关于判断可以使用状压dp,每一位0,1,?表示这个变量取0还是1或者不在集合中,我们要做的事把所有的?替换成0和1之后,看是否是同一个数字。为了方便,可以改成求取值为0和1的个数。
我们可以按?的个数从少到多进行dp,选择一个问号改成0和1,然后将0和1的个数相加。
最小的certificate和上面的方法类似,用0,1,?表示这个变量取0还是1或者不在集合中,然后记录每个状态的最小certificate,按?的个数从多到少转移,转移的时候将每个问号改成0和1。
时间复杂度O(3^n*n)
C
首先考虑给出两个点集,如何求这两个点集合并之后的直径,方法是把两个点集的直径分别求出来,然后对于这4个点,求出两两之间距离的最大值。
于是可以按dfs序建立线段树,然后求出每个区间的直径。
而对于一个询问,删掉k条边,每棵子树都对应的dfs序中的若干区间,而且区间总个数不会超过2k,对于每个区间可以在线段树中查询。
时间复杂度O(nlog^2n)。
D
首先可以发现一棵树的自同构方案数一定是若干个阶乘的乘积,所以不同的自同构方案数不会很多。
于是令dp[n][k]表示大小为n的有根树,自同构方案数为k的方案。
转移的时候可以从小到大一个一个加子树,然后考虑考虑如果一棵自同构方案为k的子树出现了c次,那么乘上在方案数上乘上c!*k^c。具体实现的时候因为子树个数可能很多,所以需要把自同构方案相同的子树合起来考虑。
最后考虑无根的情况,我们只要考虑重心即可,也就是子树大小都不能超过n/2,然后减掉双重心的情况。
由于限制很小,也可以考虑打表。