2025/7/25 模拟赛总结
\(100+100+25+35=260\),没挂分就是赢!
赛时记录:
\(3:46:03\) 写完 A(\(100\))
\(2:44:53\) B 假了
\(2:17:53\) 写完 D 15pts(\(115\))
\(1:20:11\) 写完 B(\(215\))
\(1:06:53\) 写完 D 35pts(\(235\))
\(0:50:56\) 写完 C 25pts(\(260\))
简单题,枚举左右区间的端点,答案为最大子段和的积与最小子段和的积的最大值
赛时不记得线性求前后缀最大子段和了,于是写了线段树
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 2e5 + 5;
struct P {
LL s, lt, rt, t;
};
LL T, n, a[kMaxN], ans = -1e18;
struct SegTree {
P p[kMaxN << 2];
P pushup(P l, P r) { return (P){l.s + r.s, max(l.lt, l.s + r.lt), max(r.rt, r.s + l.rt), max({l.t, r.t, l.rt + r.lt})}; }
void update(int qx, int x, int l, int r, LL k) {
if (l == r) {
return p[x] = (P){k, k, k, k}, void();
}
int mid = l + r >> 1;
qx <= mid && (update(qx, x << 1, l, mid, k), 0), qx > mid && (update(qx, x << 1 | 1, mid + 1, r, k), 0);
p[x] = pushup(p[x << 1], p[x << 1 | 1]);
}
P query(int ql, int qr, int x, int l, int r) {
if (ql <= l && r <= qr) {
return p[x];
}
int mid = l + r >> 1;
if (ql <= mid && qr > mid) {
P L = query(ql, qr, x << 1, l, mid), R = query(ql, qr, x << 1 | 1, mid + 1, r);
return pushup(L, R);
} else if (qr <= mid) {
return query(ql, qr, x << 1, l, mid);
} else if (ql > mid) {
return query(ql, qr, x << 1 | 1, mid + 1, r);
}
}
void init(int n) {
for (int i = 1; i <= n << 2; i++) {
p[i] = (P){0, 0, 0, 0};
}
}
} tr1, tr2;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
for (cin >> T; T; T--, tr1.init(n), tr2.init(n), ans = -1e18) {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i], tr1.update(i, 1, 1, n, a[i]), tr2.update(i, 1, 1, n, -a[i]);
}
for (int i = 1; i < n; i++) {
LL l = tr1.query(1, i, 1, 1, n).t, r = tr1.query(i + 1, n, 1, 1, n).t;
LL x = tr2.query(1, i, 1, 1, n).t, y = tr2.query(i + 1, n, 1, 1, n).t;
ans = max({ans, l * r, x * y});
}
cout << ans << '\n';
}
return 0;
}
比较抽象的题,写了一个小时发现题看错了,但是要改的只有两行。好多人写的都是单 \(\log\) 权值线段树,那我们双 \(\log\) 小常数树状数组二分怎么你了。绝对不是我忘记权值线段树怎么写了
对所有玩偶按 \(c\) 从大到小排序,用两个树状数组存 \(p_i\) 和 \(p_i\times c_i\) 的前缀和
从 \(5\times 10^5\) 倒序向 \(1\) 枚举,用 set 存下每个数在哪些位置出现了。设当前枚举到了 \(i\)。严格大于一半代表其他玩偶的数量总和小于 \(i\) 的数量,用数组 \(c,u\) 分别存每一种玩偶的 \(\sum p_i,\sum p_i\times c_i\)
令 tot 为所有玩偶的损失之和,\(p,q\) 为比 \(i\) 大的所有玩偶的 \(\sum p_i,\sum p_i\times c_i\)
一开始将所有 \(p_i,p_i\times c_i\) 插入树状数组。枚举过程中将这些数删掉。二分能保留的最大的玩偶,去最小值即可
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 5e5 + 5;
struct P {
LL h, c, p;
bool operator<(const P &o) const { return c != o.c ? c > o.c : h < o.h; }
} a[kMaxN];
LL n, c[kMaxN], f[kMaxN], d[kMaxN], p[kMaxN], u[kMaxN], cnt, tot, t[2][kMaxN], cur = -1, L, R, ans = 1e18, w, q;
set<int> s[kMaxN];
void update(int op, int x, LL k) {
for (; x <= n; x += x & -x) {
t[op][x] += k;
}
}
LL query(int op, int x, LL ret = 0) {
for (; x; x -= x & -x) {
ret += t[op][x];
}
return ret;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i].h >> a[i].c >> a[i].p, c[a[i].h] += a[i].p, u[a[i].h] += a[i].p * a[i].c, cnt += a[i].p, tot += a[i].p * a[i].c;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
s[a[i].h].insert(i), update(0, i, a[i].p * a[i].c), update(1, i, a[i].p);
}
for (int i = 5e5; i; i--) {
if (s[i].empty()) {
continue;
}
c[i]--;
if (cnt - (c[i] + 1) - w <= c[i]) {
ans = min(ans, q), w += c[i] + 1, q += u[i];
continue;
}
for (int pos : s[i]) {
update(1, pos, -a[pos].p), update(0, pos, -(a[pos].p * a[pos].c));
}
L = 1, R = n;
for (int mid = L + R >> 1; L < R; mid = L + R >> 1) {
query(1, mid) >= c[i] ? R = mid : L = mid + 1;
}
LL lst = query(1, L) - c[i];
f[i] = tot - (query(0, L) - a[L].c * lst) - u[i], ans = min(ans, f[i]);
w += c[i] + 1, q += u[i];
}
cout << ans << '\n';
return 0;
}
显然平着飞的次数最多为 \(1\),否则可以先向上再向下,这样一定不会更劣
于是可以从 \(1,n\) 开始做两次 Dijkstra,枚举平着飞的那一条边或没有平飞时的顶点,求 \(\min\) 即可
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int kMaxN = 2e5 + 5;
struct P {
LL u, f;
bool operator<(const P &o) const { return f > o.f; }
};
LL n, m, a[kMaxN], ans = 1e18, cur, dis[2][kMaxN], vis[kMaxN];
vector<int> g[kMaxN];
priority_queue<P> q;
void Dij(int op, int s) {
for (fill(vis, vis + kMaxN, 0), fill(dis[op], dis[op + 1], 1e18); !q.empty(); q.pop()) {
}
for (q.push({s, 0}), dis[op][s] = 0; !q.empty();) {
auto [u, f] = q.top();
q.pop();
if (!vis[u]) {
vis[u] = 1;
for (int v : g[u]) {
dis[op][v] > max(a[v], f + 1) && (q.push({v, max(a[v], f + 1)}), dis[op][v] = max(a[v], f + 1));
}
}
}
}
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1, u, v; i <= m; i++) {
cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
}
Dij(0, 1), Dij(1, n);
for (int i = 1; i <= n; i++) {
ans = min(ans, max(dis[0][i], dis[1][i]) << 1);
for (int j : g[i]) {
ans = min(ans, max(dis[0][i], dis[1][j]) << 1 | 1);
}
}
cout << ans << '\n';
return 0;
}
先考虑两个区间是包含关系,则它们要么在同一组,要么大区间单独一组,所以我们先把所有互不包含的区间求出来
令 \(dp_{i,j}\) 为考虑前 \(i\) 个区间,分成 \(j\) 段的最大总和,使用单调队列优化 dp 即可
// BLuemoon_
#include <bits/stdc++.h>
using namespace std;
const int kMaxN = 5e3 + 5;
struct P {
int l, r;
bool operator<(const P &o) const { return l != o.l ? l < o.l : r > o.r; }
} a[kMaxN], c[kMaxN];
struct S {
bool operator()(const int &l, const int &r) { return a[l].r < a[r].r; }
};
int n, k, dp[kMaxN][kMaxN], tot, p, l[kMaxN], q[kMaxN << 1], hd, tl, ans, vis[kMaxN];
priority_queue<int, vector<int>, S> Q;
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> k, fill(dp[0], dp[kMaxN], -2.1e9), dp[0][0] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i].l >> a[i].r;
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
for (; !Q.empty() && a[Q.top()].r >= a[i].r; vis[Q.top()] = 1, Q.pop()) {
}
Q.push(i);
}
for (int i = 1; i <= n; i++) {
vis[i] ? (l[++p] = a[i].r - a[i].l + 1) : (c[++tot] = a[i], 0);
}
for (int j = 1; j <= k; j++) {
q[hd = tl = 1] = 0;
for (int i = 1; i <= tot; i++) {
for (; hd <= tl && c[q[hd] + 1].r < c[i].l; hd++) {
}
dp[i][j] = dp[q[hd]][j - 1] + c[q[hd] + 1].r - c[i].l + 1;
for (; hd <= tl && dp[q[tl]][j - 1] + c[q[tl] + 1].r <= dp[i][j - 1] + c[i + 1].r; tl--) {
}
q[++tl] = i;
}
}
sort(l + 1, l + p + 1, greater<int>());
for (int i = 1, w = dp[tot][i]; i <= k; i++, w = dp[tot][i]) {
if (k - i <= p) {
for (int j = 1; j <= k - i; j++) {
w += l[j];
}
ans = max(ans, w);
}
}
cout << ans << '\n';
return 0;
}

浙公网安备 33010602011771号