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
省流:不會。