加载中...

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\) 的倍数,那么转移为:

  1. 不操作 \(u\)

\[f_{u,gcd(a[u],x)} \leftarrow \sum_{v} f_{v,x} \]

  1. 操作 \(u\)

\[f_{u,gcd(a[u],x)*gcd(a[u],x)} \leftarrow (\sum_{v} f_{v,x}) + 1 \]

其中需要注意 \(gcd(a[u],x)*gcd(a[u],x)\) 中的某些质因子个数相对于 \(a[1]\) 可能会冒,而我们只需要求 \(a[1]\) 的每个约数作为状态时的答案,因此实际上第 2 步转移式应写作:

\[f_{u,gcd(a[1],gcd(a[u],x)*gcd(a[u],x))} \leftarrow (\sum_{v} f_{v,x}) + 1 \]

注意上述转移完成后得到的 \(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\) 就是答案。

code

posted @ 2026-03-10 20:12  小橘奏  阅读(14)  评论(0)    收藏  举报