20250221

T1

穿越

考虑从叶子向上贪心,每个点实际上可以选择一个子树中的点扩展完上来,再选择一个子树中的点下去。所以每个点开一个可并堆维护这个点的所有决策,然后每个点先把儿子的合并上来再选两个最优的决策去做。实际上 set 启发式合并也可以过。

代码
#include <iostream>
#include <set>
#define int long long
using namespace std;
int n;
int a[300005];
int head[300005], nxt[600005], to[600005], ecnt;
inline void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
multiset<pair<int, int>, greater<pair<int, int> > > st[300005];
int f[300005];
void Merge(multiset<pair<int, int>, greater<pair<int, int> > > &a, multiset<pair<int, int>, greater<pair<int, int> > > &b) {
    if (a.size() > b.size()) 
        swap(a, b);
    for (auto v : a) b.insert(v);
}
void dfs(int x, int fa) {
    int cs = 0;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            ++cs;
            dfs(v, x);
            st[x].insert(make_pair(f[v], v));
        }
    }
    for (int i = 1; i <= 2 && st[x].size(); i++) {
        pair<int, int> p = *st[x].begin();
        f[x] += p.first;
        st[x].erase(st[x].begin());
        Merge(st[p.second], st[x]);
    }
    f[x] += a[x];
}
signed main() {
    freopen("travel.in", "r", stdin);
    freopen("travel.out", "w", stdout);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        ++u, ++v;
        add(u, v);
        add(v, u);
    }
    dfs(1, 0);
    cout << f[1] << "\n";
    return 0;
}

T2

树上水题

考虑如果只能选子树,则容易 \(n^2\) 做。希望尽量简单一点,可以选取重心作为根,这样如果点集的 LCA 不是根,则选的就一定是子树而不是外子树。于是只需要考虑点集的 LCA 不为根的情况,这个时候选的一定是整棵树去掉根的一棵子树形成的连通块。考虑对于每个 \(x\) 计算答案 \(\ge x\) 的方案数,答案就是所有方案数加起来。如果答案 \(\ge x\),则所有子树大小 \(\ge n - x + 1\) 的子树当中都至少需要选择一个点。考虑所有符合要求的极小子树,这些子树两两不交。我们考虑对着这个东西容斥,钦定一些极小子树不选。设所有极小子树的大小构成多重集 \(A\),则答案 \(ans = \sum\limits_{S \subseteq A}(-1)^{|S|}\binom{n - \sum\limits_{x \in A} x}{k}\)。考虑对于每个上面那个东西求出容斥系数的总和,然后对于每个 \(k\) 统一算答案。这个容易背包求出。然后随着 \(x\) 的增加,\(A\) 里东西的变化总次数是 \(\mathcal{O}(n)\) 级别,可以直接暴力更新背包。于是做完了,总复杂度 \(\mathcal{O}(n^2)\)

代码
#include <iostream>
#include <vector>
#include <array>
#define int long long
using namespace std;
const int P = 998244353;
inline void Madd(int &x, int y) { (x += y) >= P ? (x -= P) : 0; }
int fac[20005], ifac[20005], inv[20005];
void Cpre(int n) {
    fac[0] = fac[1] = ifac[0] = ifac[1] = inv[0] = inv[1] = 1;
    for (int i = 2; i <= n; i++) {
        fac[i] = fac[i - 1] * i % P;
        inv[i] = (P - P / i) * inv[P % i] % P;
        ifac[i] = ifac[i - 1] * inv[i] % P;
    }
}
inline int C(int n, int m) { return (n < 0 || m < 0 || n < m) ? 0 : fac[n] * ifac[m] % P * ifac[n - m] % P; }
int qpow(int x, int y = P - 2) {
    int ret = 1;
    while (y) {
        if (y & 1) 
            ret = ret * x % P;
        y >>= 1;
        x = x * x % P;
    }
    return ret;
}
int n;
int head[7005], nxt[20005], to[20005], ecnt;
inline void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int rt, msz;
int sz[7005], mx[7005];
void getroot(int x, int fa) {
    sz[x] = 1;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            getroot(v, x);
            sz[x] += sz[v];
            mx[x] = max(mx[x], sz[v]);
        }
    }   
    mx[x] = max(mx[x], n - sz[x]);
    if (mx[x] < msz) 
        msz = mx[x], rt = x;
}
int fa[7005];
vector<array<int, 2> > vec[7005];
void dfs(int x, int fa) {
    mx[x] = 0, sz[x] = 1; ::fa[x] = fa;
    for (int i = head[x]; i; i = nxt[i]) {
        int v = to[i];
        if (v != fa) {
            dfs(v, x);
            sz[x] += sz[v];
            mx[x] = max(mx[x], sz[v]);
        }
    }
    if (x != rt) {
        vec[sz[x]].emplace_back((array<int, 2>) { sz[x], 1 });
        vec[mx[x]].emplace_back((array<int, 2>) { sz[x], 0 });
    }
}
int cur[7005], coe[7005], ans[7005];
void ins(int x) { for (int i = 1; i <= n - x; i++) Madd(cur[i], P - cur[i + x]); }
void del(int x) { for (int i = n; i > x; i--) Madd(cur[i - x], cur[i]); }
signed main() {
    freopen("tree.in", "r", stdin);
    freopen("tree.out", "w", stdout);
    Cpre(7000);
    cin >> n;
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        add(u, v);
        add(v, u);
    }
    msz = n + 1;
    getroot(1, 0);
    dfs(rt, 0);
    for (int i = 1; i <= n; i++) {
        if (i == rt) 
            continue;
        for (int K = 1; K <= n; K++) {
            int k = sz[i];
            Madd(ans[K], k * C(sz[i], K) % P);
            for (int j = head[i]; j; j = nxt[j]) {
                int v = to[j];
                if (fa[v] == i) 
                    Madd(ans[K], P - k * C(sz[v], K) % P);
            }
        }
    }
    int mx1 = 0, mx2 = 0;
    cur[n] = 1;
    for (int i = head[rt]; i; i = nxt[i]) {
        int v = to[i];
        Madd(cur[sz[v]], P - 1);
        if (sz[v] >= mx1) 
            mx2 = mx1, mx1 = sz[v];
        else 
            mx2 = max(mx2, sz[v]);
    }
    for (int i = 1; i <= n; i++) Madd(coe[i], (n - mx[rt]) * cur[i] % P);
    for (int i = 1; i < n; i++) cur[i] = 0;
    vec[mx1].emplace_back((array<int, 2>) { n - mx1, 1 });
    vec[mx2].emplace_back((array<int, 2>) { n - mx1, 0 });
    for (int i = mx[rt]; i; i--) {
        for (auto v : vec[i]) v[1] ? ins(v[0]) : del(v[0]);
        for (int j = 1; j <= n; j++) Madd(coe[j], cur[j]);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) 
            Madd(ans[i], coe[j] * C(j, i) % P);
    }
    for (int i = 1; i <= n; i++) cout << ans[i] * qpow(C(n, i)) % P << "\n";
    return 0;
}
posted @ 2025-02-21 17:07  forgotmyhandle  阅读(33)  评论(0)    收藏  举报