ABC373 D-F 详解
D
思路
说是有向图,实际上可以看作是无向图。因为如果有
因为题目保证给出的数量关系没有冲突,所以如果我们知道了一个结点
我们可以对每个连通块任取一个结点,将它的值设为
实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 200000 + 1;
vector<pair<int, int>> graph[N];
bitset<N> vis;
int que[N];
ll x[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= m; ++i) {
int u, v, w;
cin >> u >> v >> w;
graph[u].emplace_back(v, w);
graph[v].emplace_back(u, -w);
}
for (int i = 1; i <= n; ++i) if (!vis[i]) {
int front = 1, rear = 0;
vis[que[++rear] = i] = true;
while (front <= rear) {
int u = que[front++];
for (auto [v, w] : graph[u]) if (!vis[v]) {
x[v] = x[u] + w;
vis[que[++rear] = v] = true;
}
}
}
for (int i = 1; i <= n; ++i) {
cout << x[i] << " \n"[i == n];
}
return 0;
}
E
思路
对每一位候选者
考虑每个候选者
先将
考虑优化 check,注意到 check 等价于在算这样一个式子:
因为
最终复杂度为
实现
有一些实现需要特判
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 200000 + 1;
ll a[N], b[N], pre[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;;
cin >> n >> m;
if (n == m) {
for (int i = 1; i <= n; ++i) {
cout << "0" << " \n"[i == n];
}
return 0;
}
ll k;
cin >> k;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
memcpy(b + 1, a + 1, sizeof(ll) * n);
sort(b + 1, b + n + 1);
for (int i = 1; i <= n; ++i) {
pre[i] = pre[i - 1] + b[i];
}
ll rem = k - pre[n];
auto check = [=](int x, ll mid) {
ll tar = a[x] + mid + 1;
if (lower_bound(b + 1, b + n + 1, a[x]) - b <= n - m) {
int idx = (int)(lower_bound(b + n - m + 1, b + n + 1, tar) - b - 1);
return 1ll * (idx - (n - m)) * tar - (pre[idx] - pre[n - m]) > rem - mid;
}
int idx = (int)(lower_bound(b + n - m, b + n + 1, tar) - b - 1);
return 1ll * (idx - (n - m - 1)) * tar - (pre[idx] - pre[n - m - 1]) - max(tar - a[x], 0ll) > rem - mid;
};
for (int i = 1; i <= n; ++i) {
ll l = 0, r = rem;
while (l <= r) {
ll mid = (l + r) >> 1;
if (check(i, mid)) {
r = mid - 1;
} else {
l = mid + 1;
}
}
cout << (l > rem ? -1ll : l) << " \n"[i == n];
}
return 0;
}
F
思路
相信普及组毕业的同学都有能力随手列出这样一个 dp 式子:
设
static long long f[MAXN][MAXW];
for (int i = 1; i <= n; ++i) {
for (int j = 0; w[i] * j <= W; ++j) {
for (int k = W; k >= w[i] * j; --k) {
f[i][k] = max(f[i][k], f[i - 1][k - w[i] * j] + 1ll * j * v[i] - 1ll * j * j));
}
}
}
容易得知计算量为
那如果
现实总是残酷的,但想象力丰富的人或许可以有时活在童话里。
我们应该还是小孩,天真和童趣暂未离我们而去,如果又是 OIer 那么可能还会带着一点青涩的哲思:如果可以把所有重量相同的物品取
按照这个目标我们奋进。我们自然地设出
static long long f[MAXN][MAXW];
for (int i = 1; i <= n; ++i) {
for (int j = 0; i * j <= W; ++j) {
for (int k = W; k >= i * j; --k) {
f[i][k] = max(f[i][k], f[i - 1][k - i * j] + val(i, j)));
}
}
}
其中
那么我们现在的问题就转换为了求出每个
之后我们可以实时维护一个所有重量为
形式化一点,我们设所有重量为
: .
:- 求出一个下标
使得 的值在序列 中最大. . .
- 求出一个下标
要动态支持获取最大值,删去最大值,插入一个值……我们容易想到用堆来维护
于是我们就在总复杂度为
实现
建议写 dp 时能滚动数组就滚动数组实现,缓存和空间都会友好许多。
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
constexpr int N = 3000 + 1;
vector<ll> vs[N];
ll heap[N], f[2][N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, w;
cin >> n >> w;
for (int i = 1; i <= n; ++i) {
int w, v;
cin >> w >> v;
vs[w].emplace_back(v);
}
int cur = 0;
for (int i = 1; i <= w; ++i) {
int sz = (int)vs[i].size();
if (!sz) {
continue;
}
for (int j = 0; j < sz; ++j) {
heap[j + 1] = vs[i][j] - 1;
}
make_heap(heap + 1, heap + sz + 1);//线性建堆
cur ^= 1;
memcpy(f[cur], f[!cur], sizeof(ll) * (w + 1));
ll val = 0;
for (int j = 1; j <= w / i; ++j) {
if ((val += heap[1]) <= 0ll) {
break;
}//可能没什么用的剪枝
for (int k = w; k >= i * j; --k) {
f[cur][k] = max(f[cur][k], f[!cur][k - i * j] + val);
}
pop_heap(heap + 1, heap + sz + 1);
heap[sz] -= 2;
push_heap(heap + 1, heap + sz + 1);
}
}
cout << f[cur][w] << '\n';
return 0;
}
posted on 2024-10-01 17:15 SkyWave2022 阅读(29) 评论(1) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架