泰酷辣

XSC062發表於2024-08-28

A. 城堡保衛戰

https://www.becoder.com.cn/contest/5484/problem/1

省流:\(m=3\times 10^5\) 😂

不難注意到 \(a\cdot b+(a'+d)(b'+d)<a'\cdot b'+(a+d)(b+d)\Leftrightarrow a+b>a'+b'\)。如果確定了所選的點,那麼順序只需要按 \(a+b\) 的值從大到小確定。

但是如何確定所選的點呢?很可惜,隨便列幾個式子就會發現:當所選點集大小不同時,點集內的元素根本不能確定——可能當我們需要一個大小為 \(L\) 的集合時,選擇點 \(i\) 是最優解;但當需要一個大小為 \(L+1\) 的集合時,不選 \(i\) 反而是最優解。於是不能貪了,考慮 DP。先把所有點按 \(a+b\) 降序排列,然後用 \(f_{i,j}\) 表示前 \(i\) 個裡選了 \(j\) 個的最小代價,\(n^2\) DP,對每個 \(h\) 二分找最大即可。

#include <bits/stdc++.h>
const int maxn = 3e3 + 5;
const int maxm = 3e5 + 5;
int a[maxn], b[maxn];
long long f[maxn][maxn];
long long h[maxm], t[maxn];
int main() {
#ifdef ONLINE_JUDGE
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
#endif
    freopen("hunter.in", "r", stdin);
    freopen("hunter.out", "w", stdout);
    int n, m, d;
    std::cin >> n >> m >> d;
    for (int i = 1; i <= n; ++i)
        std::cin >> a[i];
    for (int i = 1; i <= n; ++i)
        std::cin >> b[i];
    std::vector<int> u(n);
    std::iota(u.begin(), u.end(), 1);
    for (int i = 1; i <= m; ++i)
        std::cin >> h[i];
    std::sort(u.begin(), u.end(), [&](int x, int y) {
        return a[x] + b[x] > a[y] + b[y];
    });
    memset(f, 0x3f, sizeof (f));
    f[0][0] = 0;
    for (int i = 1; i <= n; ++i) {
        f[i][0] = 0;
        for (int j = 1; j <= i; ++j)
            f[i][j] = std::min(f[i - 1][j], f[i - 1][j - 1] + (long long)(a[u[i - 1]] + (j - 1) * d) * (b[u[i - 1]] + (j - 1) * d));
    }
    for (int i = 0; i <= n; ++i) {
        t[i] = f[n][i];
        // printf("f[%d] = %lld\n", i, t[i]);
    }
    for (int i = 1; i <= m; ++i)
        std::cout << std::lower_bound(t + 1, t + n + 1, h[i]) - t - 1 << ' ';
    std::cout << '\n';
    return 0;
}


B. 樹維

https://www.becoder.com.cn/contest/5484/problem/2

省流:treesiz.in.cpp 😂

\(x\) 被選中的機率為 \(1-p_x\)多於一個子樹中有點被選中 的機率之積加上 \(p_x\)

然後常規 DP,設 \(f_{i,j}\) 表示 \(i\) 引導的子樹中有 \(j\) 個兒子中有點被選中的期望點數。當然 \(f_{i,2}\) 中的 \(2\)\(\ge 2\) 的意思就是了。

#include <bits/stdc++.h>
const int maxn = 1e5 + 5;
const int mod = 998244353;
long long res;
long long p[maxn];
long long f[maxn][3];
std::vector<int> g[maxn];
long long qkp(long long x, int y) {
    long long res = 1;
    for (; y; y >>= 1, (x *= x) %= mod)
        if (y & 1)
            (res *= x) %= mod;
    return res;
}
void DFS(int x, int fa) {
    f[x][0] = 1;
    for (auto i : g[x])
        if (i != fa) {
            DFS(i, x);
            f[x][2] = (f[x][2] * (f[i][0] + f[i][1] + f[i][2]) % mod + f[x][1] * (f[i][1] + f[i][2]) % mod) % mod;
            f[x][1] = (f[x][1] * f[i][0] % mod + f[x][0] * (f[i][1] + f[i][2]) % mod) % mod;
            (f[x][0] *= f[i][0]) %= mod;
        }
    (res += p[x] + (1 + mod - p[x]) * f[x][2] % mod) %= mod;
    f[x][2] = (f[x][2] + f[x][1] * p[x] % mod) % mod;
    f[x][1] = (f[x][1] * (1 + mod - p[x]) % mod + f[x][0] * p[x] % mod) % mod;
    (f[x][0] *= (1 + mod - p[x]) % mod) %= mod;
    return;
}
int main() {
#ifdef ONLINE_JUDGE
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
#endif
    freopen("treesiz.in", "r", stdin);
    freopen("treesiz.out", "w", stdout);
    int n;
    std::cin >> n;
    for (int i = 1, a, b; i <= n; ++i) {
        std::cin >> a >> b;
        p[i] = a * qkp(b, mod - 2) % mod;
    }
    for (int i = 1, x, y; i < n; ++i) {
        std::cin >> x >> y;
        g[x].push_back(y);
        g[y].push_back(x);
    }
    DFS(1, -1);
    std::cout << res << '\n';
    return 0;
}

C. 野外旅遊

https://www.becoder.com.cn/contest/5484/problem/3

省流:\(v=u+1\) 😂

我們知道這個樹上的連通塊呢肯定是個樹,它是個樹呢它就 \(m=n-1\)

對於每條邊 \((u,v)\),先讓 \(u<v\),反正 swap 一下就行。

然後從左往右列舉右端點 \(v\),把它上面掛的所有 \(u\) 都塞到線段樹裡面去,每塞一個 \(u\) 進去判一下 \(u\sim v\) 裡的邊數是不是 \(v-u\) 就行。

笑話:我因為太懶就只寫了上面幾句,想著後面這麼簡單讀者可以自行理解,然後就去實現,然後差點不會 😅

總之,我們記當前線段樹上點 \(l\) 已經被連上的邊數為 \(val_l\),那麼我們就想知道在 \(l\sim r\) 範圍內滿足 \(\sum val_{l\sim r}=(r-l+1)-1=r-l\)\(l\) 的個數。看起來這個線段樹上計數和這個 \(\sum val_{l\sim r}\) 很不好搞,但是我們實際上發現,因為列舉了 \(r\),所以我們每把 \(r\) 向右移動一位,將此前所有 \(u_l\) 加上 \(val_r\) 即可(實際上現在這個時候 \(val_r=0\) 😂)。

至於計數,參考一下隔壁 Pudding Monsters 的解決方案,直接 \(u_l\gets u_l-r+l\),大力維護滿足 \(u_l=0\) 的元素數量。這個時候為了使數量可線上段樹上以類 Pudding Monsters 的方式維護,我們盲猜 \(0\) 一定是最大值或最小值。然後我們知道題目大背景是個樹,那麼任選一個生成子圖出來一定滿足 \(m'<n'\),即 \(u_l\le 0\),又由於 \(u_{[r, r]} = 0\),故全樹一定存在最大值 \(0\),統計最大值個數即可。

#include <bits/stdc++.h>
const int maxn = 3e5 + 5;
std::vector<int> g[maxn];
struct { int l, r, u, d, c; } t[maxn << 2];
#define lt (p << 1)
#define rt (lt | 1)
void pushup(int p) {
    t[p].u = std::max(t[lt].u, t[rt].u);
    t[p].c = 0;
    if (t[lt].u == t[p].u)
        t[p].c += t[lt].c;
    if (t[rt].u == t[p].u)
        t[p].c += t[rt].c;
    return;
}
void pushdown(int p) {
    if (t[p].d) {
        t[lt].d += t[p].d;
        t[lt].u += t[p].d;
        t[rt].d += t[p].d;
        t[rt].u += t[p].d;
        t[p].d = 0;
    }
    return;
}
void bld(int p, int l, int r) {
    t[p].l = l, t[p].r = r;
    t[p].d = 0;
    if (l == r) {
        t[p].u = l, t[p].c = 1;
        return;
    }
    int mid = (t[p].l + t[p].r) >> 1;
    bld(lt, l, mid);
    bld(rt, mid + 1, r);
    pushup(p);
    return;
}
void add(int p, int l, int r, int v) {
    if (l <= t[p].l && t[p].r <= r) {
        t[p].u += v, t[p].d += v;
        return;
    }
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1;
    if (l <= mid)
        add(lt, l, r, v);
    if (r > mid)
        add(rt, l, r, v);
    pushup(p);
    return;
}
std::pair<int, int> ask(int p, int l, int r) {
    if (l <= t[p].l && t[p].r <= r)
        return std::make_pair(t[p].u, t[p].c);
    pushdown(p);
    int mid = (t[p].l + t[p].r) >> 1, res = 0;
    if (r <= mid)
        return ask(lt, l, r);
    if (l > mid)
        return ask(rt, l, r);
    auto ls = ask(lt, l, r), rs = ask(rt, l, r);
    if (rs.first > ls.first)
        return rs;
    if (rs.first == ls.first)
        ls.second += rs.second;
    return ls;
}
int main() {
#ifdef ONLINE_JUDGE
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    std::cout.tie(nullptr);
#endif
    freopen("trip.in", "r", stdin);
    freopen("trip.out", "w", stdout);
    int T;
    for (std::cin >> T; T--; ) {
        int n;
        std::cin >> n;
        for (int i = 1, x, y; i < n; ++i) {
            std::cin >> x >> y;
            g[std::max(x, y)].push_back(std::min(x, y));
        }
        long long res = 0;
        bld(1, 1, n);
        for (int i = 1; i <= n; ++i) {
            for (auto j : g[i])
                add(1, 1, j, 1);
            add(1, 1, n, -1);
            auto tmp = ask(1, 1, i);
            // printf("(%d, %d)\n", tmp.first, tmp.second);
            res += ask(1, 1, i).second;
        }
        std::cout << res << '\n';
        for (int i = 1; i <= n; ++i)
            g[i].clear(), g[i].shrink_to_fit();
    }
    return 0;
}

D. 路在何方

https://www.becoder.com.cn/contest/5484/problem/4

省流:不會。

相關文章