bzoj 3218 a + b Problem 最小割+主席树优化建图

本文介绍了一种使用主席树优化最小割算法的方法,通过优化建图过程减少边的数量至O(nlogn),解决了原O(n^2)复杂度带来的时间和空间问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题面

题目传送门

解法

  • 比较显然的最小割,不妨考虑如何建图。
  • 首先将 S S S连向每一个点 i i i,容量为 w [ i ] w[i] w[i],表示割这条边点 i i i的颜色为黑色; i i i T T T连容量为 b [ i ] b[i] b[i]的边,表示割这条边点 i i i的颜色为白色。对于 j j j满足 1 ≤ j &lt; i 1\leq j&lt;i 1j<i a [ j ] ∈ [ l [ i ] , r [ i ] ] a[j]\in [l[i],r[i]] a[j][l[i],r[i]],连接 i ′ i&#x27; i,容量为 ∞ \infty ;最后每一个 i ′ i&#x27; i i i i连一条容量为 p [ i ] p[i] p[i]的边,这样我们就可以表示出一个奇怪的点的情况了。
  • 但是这样建图的话边数是 O ( n 2 ) O(n^2) O(n2)的,时间和空间都无法承受。
  • 然后我们可以发现,使 i i i可能成为奇怪的点的 j j j,满足一个二维数点的关系,并且下标的范围为一段前缀。
  • 那么我们可以考虑用主席树来优化这个过程,即每一次连边的时候用一段区间和当前的 i ′ i&#x27; i连边。对于主席树上的节点,从下向上连接容量为 ∞ \infty 的边即可。
  • 最后边数就被我们优化成 O ( n log ⁡ n ) O(n\log n) O(nlogn)了,问题得到解决。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
template <typename T> void read(T &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
const int N = 10010, M = N * 15; const ll inf = 1ll << 60;
int cnt, tot, rt[N], b[M], l[M], lc[M], rc[M], cur[M], head[M];
struct Info {int a, b, w, l, r, p;} q[N];
struct Edge {int next, num; ll c;} e[M * 3];
void add(int x, int y, ll c) {e[++cnt] = (Edge) {head[x], y, c}, head[x] = cnt;}
void Add(int x, int y, ll c) {add(x, y, c), add(y, x, 0);}
bool bfs(int s, int t) {
	for (int i = s; i <= tot; i++) l[i] = -1;
	queue <int> q; q.push(s), l[s] = 0;
	while (!q.empty()) {
		int x = q.front(); q.pop();
		for (int p = head[x]; p; p = e[p].next) {
			int k = e[p].num; ll c = e[p].c;
			if (c && l[k] == -1) l[k] = l[x] + 1, q.push(k);
		}
	}
	return l[t] != -1;
}
ll dfs(int x, int t, ll lim) {
	if (x == t) return lim; ll ret = 0;
	for (int &p = cur[x]; p; p = e[p].next) {
		int k = e[p].num; ll c = e[p].c;
		if (c && l[k] == l[x] + 1) {
			ll w = dfs(k, t, min(lim - ret, c));
			e[p].c -= w, e[p ^ 1].c += w, ret += w;
			if (ret == lim) return ret;
		}
	}
	if (!ret) l[x] = -1; return ret;
}
ll dinic(int s, int t) {
	ll ret = 0;
	while (bfs(s, t)) {
		for (int i = s; i <= tot; i++) cur[i] = head[i];
		ret += dfs(s, t, inf);
	}
	return ret;
}
int ins(int k, int l, int r, int x, int id) {
	int ret = ++tot; lc[ret] = lc[k], rc[ret] = rc[k];
	if (l == r) {
		Add(id, ret, inf);
		if (k) Add(k, ret, inf);
		return ret;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) lc[ret] = ins(lc[k], l, mid, x, id);
		else rc[ret] = ins(rc[k], mid + 1, r, x, id);
	if (lc[ret]) Add(lc[ret], ret, inf);
	if (rc[ret]) Add(rc[ret], ret, inf);
	return ret;
}
void query(int k, int l, int r, int L, int R, int id) {
	if (!k) return;
	if (L <= l && r <= R) return Add(k, id, inf), void();
	int mid = (l + r) >> 1;
	if (L <= mid) query(lc[k], l, mid, L, R, id);
	if (R > mid) query(rc[k], mid + 1, r, L, R, id);
}
int main() {
	int n, len = 0; read(n); ll ans = 0;
	int s = 0, t = 2 * n + 1; cnt = 1, tot = 2 * n + 1;
	for (int i = 1; i <= n; i++) {
		read(q[i].a), read(q[i].b), read(q[i].w);
		read(q[i].l), read(q[i].r), read(q[i].p);
		ans += q[i].b + q[i].w;
		Add(s, i, q[i].w), Add(i, t, q[i].b), Add(i + n, i, q[i].p);
		b[++len] = q[i].a, b[++len] = q[i].l, b[++len] = q[i].r;
	}
	sort(b + 1, b + len + 1), len = unique(b + 1, b + len + 1) - b - 1;
	for (int i = 1; i <= n; i++)
		q[i].a = lower_bound(b + 1, b + len + 1, q[i].a) - b,
		q[i].l = lower_bound(b + 1, b + len + 1, q[i].l) - b,
		q[i].r = lower_bound(b + 1, b + len + 1, q[i].r) - b;
	for (int i = 1; i <= n; i++) {
		query(rt[i - 1], 1, len, q[i].l, q[i].r, i + n);
		rt[i] = ins(rt[i - 1], 1, len, q[i].a, i);
	}
	cout << ans - dinic(s, t) << "\n";
	return 0;
}
<think>好的,用户现在问的是`torch.cuda.empty_cache()`是否只能清空一个GPU的缓存。我需要先回忆一下PyTorchCUDA内存管理的机制。 首先,用户之前遇到了关于Scikit-learn的分类指标警告和PyTorch的NCCL支持问题,现在转向了CUDA内存管理。可能用户正在处理多GPU训练,或者在单GPU使用中遇到了显存问题。 `torch.cuda.empty_cache()`的作用是释放当前由缓存分配器持有的未使用内存PyTorch默认情况下会缓存一部分内存以提高分配效率,这个函数可以释放那些未被使用的缓存内存。但用户的问题是,这个函数是否只影响当前的一个GPU。 根据PyTorch的文档,当使用多GPU时,每个GPU都有自己的缓存分配器。所以,如果用户有多个GPU,`torch.cuda.empty_cache()`只会释放当前设备(即通过`torch.cuda.current_device()`指定的那个)的缓存。要清空所有GPU的缓存,需要遍历每个设备,切换当前设备,然后分别调用该函数。 用户可能是在多GPU环境下运行代码,发现显存没有完全释放,或者遇到显存不足的问题。需要确认用户是否在使用多GPU,以及是否正确地切换了设备来清空每个GPU的缓存。 另外,用户可能对PyTorch内存管理机制不太熟悉,所以需要解释清楚这个函数的行为,以及在不同情况下的正确使用方法。可能还需要提醒用户,即使调用了这个函数,显存的释放也依赖于是否有未使用的缓存,实际使用的张量不会被释放。 最后,给出示例代码,说明如何为所有GPU清空缓存,并强调正确使用设备切换的重要性。还要提醒用户,显存管理需要结合其他方法,如手动删除变量、运行垃圾回收等,才能有效管理显存使用。</think>--- 在 PyTorch 中,`torch.cuda.empty_cache()` 的作用是释放当前 CUDA 设备(即当前选中的 GPU)的**未使用缓存内存**。其行为取决于 **GPU 设备的选择**和 **多 GPU 环境**的配置。以下是详细说明: --- ### **1.GPU 场景** - 如果你只有一个 GPU,或者代码中未显式指定 GPU 设备: `torch.cuda.empty_cache()` 会清空当前默认 GPU 的缓存内存。 例如: ```python import torch # 默认使用 GPU 0(仅单卡时) a = torch.randn(1000, 1000, device="cuda") # 占用显存 del a # 删除变量 torch.cuda.empty_cache() # 释放 GPU 0 的未使用缓存 ``` --- ### **2.GPU 场景** - 如果你有多个 GPU,且代码中显式切换了设备: 需要**依次选中每个 GPU 并单独调用** `empty_cache()`,才能清空所有 GPU 的缓存。 例如: ```python import torch # 清空 GPU 0 的缓存 torch.cuda.set_device(0) torch.cuda.empty_cache() # 清空 GPU 1 的缓存 torch.cuda.set_device(1) torch.cuda.empty_cache() ``` --- ### **3. 关键注意事项** 1. **缓存释放的范围**: `empty_cache()` **仅释放由 PyTorch 缓存分配器管理的未占用内存**,不会释放正在被张量占用的显存。 - 已分配的张量必须手动删除(如 `del tensor`)或超出作用域后,其显存才会被缓存分配器回收。 - 调用 `empty_cache()` 后,这些回收的内存才会真正释放回系统。 2. **多进程分布式训练**: 在分布式训练(如使用 `torch.distributed` 或 `DataParallel`)时,每个进程可能绑定到不同的 GPU。 - 每个进程需独立调用 `empty_cache()` 清理自己绑定的 GPU 缓存。 - 例如: ```python # 每个进程仅清理自己绑定的 GPU torch.cuda.empty_cache() ``` 3. **自动缓存管理**: PyTorch 默认会缓存部分显存以提高分配效率。频繁调用 `empty_cache()` 可能导致性能下降,建议仅在显存不足时手动调用。 --- ### **4. 验证显存释放** 可以使用 `torch.cuda.memory_summary()` 或以下代码查看显存状态: ```python import torch # 查看当前 GPU 的显存使用情况(单位:字节) print(torch.cuda.memory_allocated()) # 当前已分配的显存 print(torch.cuda.memory_reserved()) # 当前缓存分配器保留的显存(包括未使用的) ``` --- ### **总结** | 场景 | 行为 | |------------|----------------------------------------------------------------------| | **单 GPU** | 清空当前 GPU 的未使用缓存。 | | **多 GPU** | 需遍历所有 GPU,分别调用 `empty_cache()` 才能清空每个设备的缓存。 | --- ### **最佳实践** - 显存不足时手动调用 `empty_cache()`,但避免在循环中频繁使用。 - 结合显存监控工具(如 `nvidia-smi` 或 PyTorch 内置函数)诊断显存泄漏。 - 多 GPU 场景显式指定设备并分别清理: ```python for i in range(torch.cuda.device_count()): torch.cuda.set_device(i) torch.cuda.empty_cache() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值