3.10 —— cf div2 848 F (答案放状态内的dp + 子集和dp)
F. Maximizing Root
每日一题 day2
显然一定是由下到上操作,最后对根结点 \(1\) 操作。
由于 \(k \le n\),不可以在做树形 dp 时把操作数作为状态。而最终答案可能乘的数一定是 \(a[1]\) 的约数,\(a[i] \le 1000\),经估算不同约数的数量最多为 \(32\),因此答案的可能性是很少的。故我们不妨将答案设置成 dp 状态,操作次数设置为 dp 对象,根据 dp 值是否 \(\le k\) 来决定当前状态是否可行。
设 \(f_{u,i}\):考虑子树 \(u\),使子树 \(u\) 内所有元素的 \(gcd\) 是 \(i\) 的倍数,最少操作次数。其中 \(i\) 只可能为 \(a[1]\) 的约数,最多 \(32\) 种取值。
显然一定是对所有儿子 \(v\) 操作完之后,再决策是否对 \(u\) 操作。
假设 \(u\) 的所有子树 \(v\) 的 \(gcd\) 都变成了 \(x\) 的倍数,那么转移为:
- 不操作 \(u\):
- 操作 \(u\):
其中需要注意 \(gcd(a[u],x)*gcd(a[u],x)\) 中的某些质因子个数相对于 \(a[1]\) 可能会冒,而我们只需要求 \(a[1]\) 的每个约数作为状态时的答案,因此实际上第 2 步转移式应写作:
注意上述转移完成后得到的 \(f_{u,d}\) 并不是按照状态定义的答案(此时仍代表 \(d\) 本身,还不是 \(d\) 的倍数),我们还需要对每一个 \(f_{u,d}\) 做一遍关于约数的 子集和 \(dp\),以让每个 \(f_{u,d}\) 代表关于 \(d\) 的倍数的答案。
在代码中体现为:
for(int i = factor.size() - 1; i >= 0; i--){
int x = factor[i];
for(auto p : prime)
if (x % p == 0) f[u][id[x / p]] = min(f[u][id[x / p]], f[u][i]);
}
只需要按照 \(a[1]\) 的质因子划分子集就可以了:只需要从自身的所有极大真子集转移过来,那么每次只需要去除掉自身集合的某个质因子就行了。思想与树上 \(dp\) 非常类似。
找到满足 \(f_{1,D} + 1\) \(\le k\) 的最大的 \(D\),那么 \(a[1] * D\) 就是答案。

浙公网安备 33010602011771号