泰酷辣

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

省流:不会。

posted @ 2024-08-28 00:31  XSC062  阅读(33)  评论(0编辑  收藏  举报