P9132 [USACO23FEB] Watching Cowflix P 题解

如果有 \(C\) 个连通块,选取了 \(b\) 个点,答案就是 \(Ck + b\)

所以 \(C = 1\) 时,答案最多是 \(k + n\)

对于 \(C > 1\),如果答案比 \(k + n\) 优,则需要 \(Ck + b \le k + n\)\(k\le \dfrac{n}{C-1}\)

考虑根号分治,如果 \(k\le \sqrt n\),用暴力 DP,\(f_{i,0/1}\) 表示以 \(i\) 为根的子树,\(i\) 是否选取,最小代价,这一部分是 \(O(n\sqrt n)\)

如果 \(k > \sqrt n\),则 \(C\le \sqrt n + 1\),所以定义 \(h_{i, j, 0/1}\) 表示以 \(i\) 为根的子树,当前分为了 \(j\) 个连通块,\(i\) 是否选取,最小点数,最后枚举有多少个连通块即可。

根据树形背包的复杂度分析,容量是 \(m < n\) 的背包,复杂度是 \(O(nm)\),所以这一部分是 \(O(n\sqrt n)\)

证明:考虑 DFN 上两个子树的合并,他们在 DFN 上一定相邻,钦定 认为一次合并是把左子树的最靠右的 \(m\) 个和右子树最靠左的 \(m\) 个合并,所以只有在序列上距离不超过 \(2m-1\) 的点对之间,可能有贡献。
所以总复杂度是 \(O(n\sqrt n)\) 的。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
//#define int long long
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 2e5 + 10, INF = 1e9, B = 450;

bool Mst;
int n, dfn[N], pos[N], fa[N], sz[N], timestamp, f[N][2], res[N];
bool s[N];
vector<int> g[N], h[N][2];
void dfs(int u, int fat) {
    sz[u] = 1, dfn[u] = ++ timestamp, pos[timestamp] = u, fa[u] = fat;
    for(auto v : g[u]) if(v != fat) dfs(v, u), sz[u] += sz[v];
}
void cmin(int &a, int b) {
    a = (a < b ? a : b);
}
bool Med;
signed main() {
//    freopen("ex_data1.in", "r", stdin);
    ios::sync_with_stdio(0), cin.tie(0);
    cerr << abs(&Mst - &Med) / 1048576. << "Mb\n";
    cin >> n;
    for(int i = 1; i <= n; i ++) {
        char ch; cin >> ch; s[i] = ch - '0';
    }
    for(int i = 1, a, b; i < n; i ++)
        cin >> a >> b, g[a].push_back(b), g[b].push_back(a);
    dfs(1, 0);
    for(int k = 1; k <= min(B, n); k ++) {
        for(int i = 1; i <= n; i ++) f[i][0] = 0, f[i][1] = 1;
        for(int i = n, u; i; i --) {
            u = pos[i];
            if(s[u]) f[u][0] = INF;
            f[fa[u]][1] += min(f[u][0], f[u][1]), f[fa[u]][0] += min(f[u][0], f[u][1] + k);
        }
        cout << min(f[1][0], f[1][1] + k) << '\n';
    }
    int m = n / B + 1;
    for(int i = n, u; i; i --) {
        u = pos[i], h[u][0].resize(min(m, sz[u]) + 2, INF), h[u][1].resize(min(m, sz[u]) + 2, INF);
        if(!s[u]) h[u][0][0] = 0;
        h[u][1][1] = 1;
        int sum = 1;
        for(auto v : g[u]) {
            if(v != fa[u]) {
                vector<int> tmp[2]; tmp[0] = h[u][0], tmp[1] = h[u][1];
                h[u][0].assign(h[u][0].size(), INF), h[u][1].assign(h[u][1].size(), INF);
                for(int i = 0; i <= min(m, sum); i ++) {
                    for(int j = 0; j < h[v][0].size() && i + j - 1 <= min(m, sz[u]); j ++) {
                        if(!s[u]) cmin(h[u][0][i + j], min(h[v][1][j], h[v][0][j]) + tmp[0][i]);
                        cmin(h[u][1][i + j], h[v][0][j] + tmp[1][i]);
                        cmin(h[u][1][i + j - 1], h[v][1][j] + tmp[1][i]);
                    }
                }
                sum += sz[v];
                h[v][0] = vector<int>(), h[v][1] = vector<int>();
            }
        }
    }
    for(int i = 0; i <= min(m, n); i ++) res[i] = min(h[1][0][i], h[1][1][i]);
    for(int k = B + 1; k <= n; k ++) {
        int ans = INF; 
        for(int i = 0; i <= min(m, n / k + 1); i ++)
            if(ans > i * k + res[i]) ans = i * k + res[i];
        cout << ans << '\n';
    }
    
    return 0;
}
posted @ 2025-05-28 10:35  MoyouSayuki  阅读(22)  评论(0)    收藏  举报
:name :name