泰酷辣
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
省流:不会。
—— · EOF · ——
真的什么也不剩啦 😖