CF997E. Good Subsegments(线段树,单调栈)

本文介绍CF997E.GoodSubsegments题目的一种高效解法,利用单调栈维护区间信息,通过离线询问的方法处理区间询问,最终达到O(mlgn)的时间复杂度。

CF997E. Good Subsegments

Description

给定一个序列,多次询问一个区间内的连续段个数。
n,q≤2×105n,q \leq 2\times 10^{5}n,q2×105

Solution

算得上是经典题了吧。

Part one

先考虑求全部的连续段个数。

相当于求区间内mx−mn+1−len=0mx - mn + 1 - len = 0mxmn+1len=0的子区间个数,且显然有mx−mn+1−len≥0mx - mn + 1 - len \geq 0mxmn+1len0,因此相当于维护最小值及其个数。于是我们考虑从前往后枚举区间右端点rrr,维护所有右端点为rrr的区间的信息。

不难观察到前缀mxmxmx和前缀mnmnmn的单调性,每次我们加入一个prp_rpr时,会让一段后缀的mxmxmx和一段后缀的mnmnmn改变,且改变的这一段后缀mxmxmx会变成prp_rprmnmnmn同理。于是我们用单调栈分别维护前缀mx,mnmx,mnmx,mn的连续段,每次弹栈的时候做区间加就可以维护我们需要的信息了。

这样的时间复杂度是O(mlgn)O(mlgn)O(mlgn)的。

Part two

再考虑区间询问,上述枚举右端点的做法启示我们离线询问。考虑每个右端点的询问,限制[l,r][l,r][l,r]相当于是只能统计左端点在lll后面的连续段。

然后我们考虑维护clc_lcl表示当前右端点为rrr(枚举的右端点),左端点为lll的连续段个数,然后询问就是一段区间和。这个直接在线段树上打tagtagtag,表示这个区间的最小值为000的点都有多少次新增的贡献即可维护。

实现时注意下传tagtagtag的标记即可。
时间复杂度O(mlgn)O(mlgn)O(mlgn)

Code

#include <bits/stdc++.h>
 
using namespace std;
 
template<typename T> inline bool upmin(T &x, T y) { return y < x ? x = y, 1 : 0; }
template<typename T> inline bool upmax(T &x, T y) { return x < y ? x = y, 1 : 0; }
 
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
 
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int, int> PR;
typedef vector<int> VI; 
 
const lod eps = 1e-9;
const lod pi = acos(-1);
const int oo = 1 << 30;
const ll loo = 1ll << 62;
const int mods = 1e9 + 7;
const int MAXN = 300005;
const int INF = 0x3f3f3f3f; //1061109567
/*--------------------------------------------------------------------*/
 
namespace FastIO{
	constexpr int SIZE = (1 << 21) + 1;
	int num = 0, f;
	char ibuf[SIZE], obuf[SIZE], que[65], *iS, *iT, *oS = obuf, *oT = obuf + SIZE - 1, c;
	#define gc() (iS == iT ? (iT = ((iS = ibuf) + fread(ibuf, 1, SIZE, stdin)), (iS == iT ? EOF : *iS ++)) : *iS ++)
	inline void flush() {
		fwrite(obuf, 1, oS - obuf, stdout);
		oS = obuf;
	}
	inline void putc(char c) {
		*oS ++ = c;
		if (oS == oT) flush();
	}
	inline void getc(char &c) {
		for (c = gc(); c < 'A' || c > 'Z' ; c = gc());
	}
	template<class I>
	inline void read(I &x) {
		for (f = 1, c = gc(); c < '0' || c > '9' ; c = gc()) if (c == '-') f = -1;
		for (x = 0; c >= '0' && c <= '9' ; c = gc()) x = (x << 3) + (x << 1) + (c & 15);
		x *= f;
	}
	template<class I>
	inline void print(I x) {
		if (x < 0) putc('-'), x = -x;
		if (!x) putc('0');
		while (x) que[++ num] = x % 10 + 48, x /= 10;
		while (num) putc(que[num --]);
	}
	struct Flusher_{~Flusher_(){flush();}} io_Flusher_;
}
using FastIO :: read;
using FastIO :: print;
using FastIO :: putc;

ll Ans[MAXN], c[MAXN << 2], tagc[MAXN << 2];
int MX = 0, tag[MAXN << 2], mn[MAXN << 2], s[MAXN << 2], stkmx[MAXN], stkmn[MAXN], p[MAXN];
void up(int x) {
	mn[x] = min(mn[x << 1], mn[x << 1 | 1]);
	s[x] = (mn[x << 1] == mn[x]) * s[x << 1] + (mn[x << 1 | 1] == mn[x]) * s[x << 1 | 1];
}
void down(int x) {
	if (tag[x] != 0) {
		mn[x << 1] += tag[x], mn[x << 1 | 1] += tag[x]; 
		tag[x << 1] += tag[x], tag[x << 1 | 1] += tag[x];
		tag[x] = 0;
	}
	if (tagc[x] != 0) {
		if (mn[x << 1] == mn[x]) c[x << 1] += tagc[x] * s[x << 1], tagc[x << 1] += tagc[x];
		if (mn[x << 1 | 1] == mn[x]) c[x << 1 | 1] += tagc[x] * s[x << 1 | 1], tagc[x << 1 | 1] += tagc[x];
		tagc[x] = 0;
	}
}
void build(int x, int l, int r) {
	if (l == r) { mn[x] = r - l + 1, s[x] = 1; return; }
	int mid = (l + r) >> 1;
	build(x << 1, l, mid);
	build(x << 1 | 1, mid + 1, r);
	up(x);
}
void update(int x, int l, int r, int L, int R, int y) {
	if (l >= L && r <= R) { mn[x] += y, tag[x] += y;  return; }
	down(x);
	int mid = (l + r) >> 1;
	if (R <= mid) update(x << 1, l, mid, L, R, y);
	else if (L > mid) update(x << 1 | 1, mid + 1, r, L, R, y);
	else update(x << 1, l, mid, L, mid, y), update(x << 1 | 1, mid + 1, r, mid + 1, R, y);
	up(x);
}
ll query(int x, int l, int r, int L, int R) {
	if (l >= L && r <= R) return c[x];
	down(x);
	int mid = (l + r) >> 1;
	if (R <= mid) return query(x << 1, l, mid, L, R);
	else if (L > mid) return query(x << 1 | 1, mid + 1, r, L, R);
	else return query(x << 1, l, mid, L, mid) + query(x << 1 | 1, mid + 1, r, mid + 1, R);
}
struct Qnode{ int l, r, id; } Q[MAXN];
signed main() {
#ifndef ONLINE_JUDGE
	freopen("a.in", "r", stdin);
#endif
	int n, m; 
	read(n);
	for (int i = 1; i <= n ; ++ i) read(p[i]);
	read(m);
	for (int i = 1; i <= m ; ++ i) read(Q[i].l), read(Q[i].r), Q[i].id = i;
	
	sort(Q + 1, Q + m + 1, [&](Qnode x, Qnode y){ return x.r < y.r; });
	build(1, 1, n);	
	for (int i = 1, topmx = 0, topmn = 0, nw = 1; i <= n ; ++ i) {
		while (topmx && p[stkmx[topmx]] < p[i]) update(1, 1, n, stkmx[topmx - 1] + 1, stkmx[topmx], -p[stkmx[topmx]]), -- topmx;
		while (topmn && p[stkmn[topmn]] > p[i]) update(1, 1, n, stkmn[topmn - 1] + 1, stkmn[topmn], p[stkmn[topmn]]), -- topmn;
		update(1, 1, n, 1, i, -1);
		update(1, 1, n, stkmx[topmx] + 1, i, p[i]);
		update(1, 1, n, stkmn[topmn] + 1, i, -p[i]);
		stkmx[++ topmx] = i;
		stkmn[++ topmn] = i;
		down(1), ++ tagc[1], c[1] += s[1];
		while (nw <= m && Q[nw].r == i) Ans[Q[nw].id] = query(1, 1, n, Q[nw].l, i), ++ nw;
	}
	for (int i = 1; i <= m ; ++ i) print(Ans[i]), putc('\n');
	return 0;
}

from pulp import * import pandas as pd # 模拟原材料数据(增加部分长材料,降低缺陷影响) data = { '原材料长度(米)': [5.5, 5.5, 6.2, 7, 7, 5.8, 6.5, 7.5, 6, 8.2, 6.8, 6.8, 5.6, 7.3, 6.1, 8, 5.9, 6.3, 7.8, 6.7, 5.4, 7.4, 6.9, 8.1, 7.6, 5.7, 6.4, 8.3, 6, 7.9, 5.5, 6.2, 7.1, 6.8, 5.8, 7.3, 6.9, 7.5, 5.6, 6.4, 6.6, 7, 8, 5.9, 7.7, 6.5, 7.2, 6.1, 5.4, 8.2, 6.7, 7.8, 5.5, 6.6, 7, 5.8, 8, 6.9, 7.2, 6.3, 8.1, 5.6, 7.4, 6.1, 6.8, 7.3, 5.7, 7, 6.5, 5.8, 8.2, 7.5, 6, 7.7, 6.6, 6.2, 7.3, 5.5, 7, 6.9, 8, 7.4, 6.3, 10, 10, 10], # 新增3根10m材料 '缺陷位置(米)': [1, 3, 2, 1.5, 4, 1.2, 2.3, 1, 2.8, 1.3, 2.1, 5, 1.1, 3.1, 1.7, 2.5, 3, 1.9, 1.2, 2.4, 0.8, 3, 2, 2.2, 1.6, 2.7, 1.8, 0.9, 1.1, 2.9, 1.3, 3.2, 2.3, 1.9, 2.5, 3, 2, 1.6, 1, 2.2, 2, 3.1, 1.5, 1.9, 2.6, 1.1, 2.7, 3, 1.5, 2, 2.9, 1.2, 2.1, 3.2, 1.7, 1, 2.3, 2.5, 3, 2.4, 1.9, 3.1, 2, 1.8, 2.1, 1.4, 2.6, 2.5, 3, 1.2, 2.7, 3, 1.1, 2.3, 2.1, 3.1, 1.5, 2.4, 1.8, 2.5, 2.6, 1.7, 3, 0, 0, 0], # 新增材料无缺陷 '缺陷长度(米)': [0.3, 0.2, 0.4, 0.2, 0.3, 0.5, 0.3, 0.6, 0.4, 0.5, 0.3, 0.2, 0.2, 0.4, 0.5, 0.3, 0.4, 0.3, 0.4, 0.3, 0.3, 0.2, 0.5, 0.4, 0.3, 0.4, 0.2, 0.3, 0.5, 0.2, 0.4, 0.3, 0.5, 0.2, 0.4, 0.3, 0.2, 0.4, 0.3, 0.5, 0.4, 0.3, 0.3, 0.2, 0.3, 0.5, 0.4, 0.3, 0.4, 0.2, 0.4, 0.3, 0.2, 0.4, 0.3, 0.3, 0.4, 0.5, 0.4, 0.3, 0.2, 0.4, 0.3, 0.5, 0.2, 0, 0, 0], # 新增材料无缺陷 '单价(元/根)': [17, 17.33, 20.59, 24.41, 24.05, 17.33, 22, 24.77, 19.83, 27.64, 23.32, 23.69, 17.66, 24.77, 19.83, 27.64, 18, 21.27, 26.57, 22.91, 16.68, 25.85, 22.91, 27.64, 26.2, 17.33, 22, 28.72, 18, 27.64, 16.68, 20.95, 23.69, 23.69, 17.66, 25.13, 24.05, 25.49, 17.33, 20.95, 22, 24.05, 28, 19.83, 25.85, 22.41, 24.41, 20.59, 17, 27.64, 22.91, 26.57, 16.36, 22, 24.05, 17.66, 28, 23.69, 24.41, 21.27, 27.27, 17, 25.49, 19.83, 23.69, 25.13, 17.33, 24.41, 22, 17.33, 28, 25.85, 19.41, 25.85, 22, 21.27, 25.13, 16.68, 23.32, 23.69, 27.27, 25.85, 20.59, 30, 30, 30] # 新增材料单价 } max_len = max(len(v) for v in data.values()) for k in data: data[k] += [None] * (max_len - len(data[k])) raw_materials = pd.DataFrame(data) # 生成可用子段(简化逻辑,允许无缺陷材料整根使用) def generate_subsegments(row): L = row['原材料长度(米)'] if pd.isna(L) or L <= 0: return [] s = row['缺陷位置(米)'] if not pd.isna(row['缺陷位置(米)']) else 0 d = row['缺陷长度(米)'] if not pd.isna(row['缺陷长度(米)']) else 0 if s == 0 and d == 0: # 无缺陷时整根可用 return [L] segments = [] if s > 0: segments.append((0, s)) if (s + d) < L: segments.append((s + d, L)) sub_lengths = [end - start for start, end in segments if (end - start) > 0.001] return sub_lengths raw_materials['subsegments'] = raw_materials.apply(generate_subsegments, axis=1) # 订单需求(降低部分数量,或增加长材料需求) orders = [ { 'lengths': [1.6, 2.2, 1.8, 2.4, 1.7, 2.3, 1.5, 2.0], 'quantities': [200, 200, 140, 140, 100, 100, 60, 60], # 降低需求数量 'per_set': {l: 1 for l in [1.6, 2.2, 1.8, 2.4, 1.7, 2.3, 1.5, 2.0]} } ] required_lengths = orders[0]['lengths'] required_quantities = {l: q for l, q in zip(orders[0]['lengths'], orders[0]['quantities'])} per_set = orders[0]['per_set'] # 创建优化问题(移除锯口损耗,先验证可行性) prob = LpProblem("Optimal_Cutting_Problem", LpMinimize) x = LpVariable.dicts('x', [(idx, j, l) for idx in raw_materials.index for j, sub_len in enumerate(raw_materials.loc[idx, 'subsegments']) for l in required_lengths if sub_len >= l], lowBound=0, cat='Integer') y = LpVariable.dicts('y', raw_materials.index, cat='Binary') # 目标函数 prob += lpSum([y[idx] * raw_materials.loc[idx, '单价(元/根)'] for idx in raw_materials.index]) # 约束1:满足订单需求 for l, q in required_quantities.items(): prob += lpSum([x[(idx, j, l)] for idx in raw_materials.index for j in range(len(raw_materials.loc[idx, 'subsegments'])) if (idx, j, l) in x]) >= q # 约束2:子段长度限制(暂时移除锯口损耗) for (idx, j, l) in x: sub_len = raw_materials.loc[idx, 'subsegments'][j] prob += l * x[(idx, j, l)] <= sub_len # 暂时不考虑锯口,验证可行性 # 约束3:材料使用与切割关联 for (idx, j, l) in x: prob += x[(idx, j, l)] <= y[idx] * (raw_materials.loc[idx, 'subsegments'][j] // l) # 求解问题 solver = PULP_CBC_CMD(msg=False) prob.solve(solver) # 解析结果 if prob.status == LpStatusOptimal: material_cuts = {} for idx in raw_materials.index: if y[idx].value() == 1: cuts = {} subsegments = raw_materials.loc[idx, 'subsegments'] for j in range(len(subsegments)): for l in required_lengths: if (idx, j, l) in x: count = x[(idx, j, l)].value() if count > 0: if l not in cuts: cuts[l] = 0 cuts[l] += count material_cuts[idx] = cuts total_pieces = {l: sum(cuts.get(l, 0) for cuts in material_cuts.values()) for l in required_lengths} total_sets = min(total_pieces[l] // per_set[l] for l in required_lengths) print("============= 切割结果 ==============") for idx, cuts in material_cuts.items(): print(f"材料 {idx} (长度: {raw_materials.loc[idx, '原材料长度(米)']}m):") for l, cnt in cuts.items(): print(f" - 切割 {cnt} 根 {l}m 部件") print() print("============= 总套数 ==============") print(f"可组成完整套数: {total_sets} 套") print(f"各部件总数量: {total_pieces}") else: print("未找到可行解,尝试调整原材料或订单需求") 修改代码,不可能没有最优解
最新发布
05-12
<think>嗯,用户问的是如何调整线性规划代码逻辑或约束条件,确保在原材料和订单需求的情况下总有可行解。首先,我需要回忆一下线性规划的基本知识,可行解的存在条件。线性规划问题要有可行解,首先约束条件必须相容,也就是存在至少一个解满足所有约束。如果用户遇到不可行的情况,可能是因为约束太严格或者资源不足。 接下来,我需要考虑用户可能的场景。用户可能在处理生产计划或者资源分配问题,比如用线性规划来优化生产,但原材料有限,订单需求可能超过供应,导致问题不可行。这时候需要调整模型,使得无论如何都能找到可行解,可能需要松弛某些约束,或者引入新的变量来处理资源不足的情况。 用户提到的参考引用里有初始解生成、障碍法、预处理和并行加速,这可能和分布式计算有关,比如使用Spark-LP。但用户的问题更偏向于修改模型本身,而不是算法实现。所以重点应该在模型调整上。 首先,检查约束条件是否冲突。比如订单需求的总量是否超过原材料的总供应量。如果是,那么硬约束下问题不可行。解决办法可以是允许部分订单不满足,或者允许原材料不足时采购额外的资源,或者调整需求。 然后,可能需要引入松弛变量,把硬约束变成软约束,允许违反,但需要付出代价。比如在目标函数中惩罚这些松弛,这样即使资源不足,问题依然可行,但解决方案可能需要支付额外成本。 另外,可以考虑优先级处理,比如优先满足某些订单,或者将订单分成必须满足和可选满足的部分。这样在资源不足时,至少满足必须的部分,而可选部分可以调整。 还有可能引入库存变量,允许使用之前的库存,或者将未使用的资源存储到未来,但这可能涉及到多周期问题,用户的问题可能只针对单周期。 参考引用中的障碍法,可能处理非线性约束,但用户的问题是线性的,所以障碍法可能不适用。不过障碍法的思想是将约束转化为目标函数的一部分,这可能启发使用惩罚项的方式。 预处理步骤中的线性代数运算,比如LU分解,可能用于提高求解效率,但用户的问题是关于可行解的,所以可能需要调整问题结构,比如增加松弛变量,重新定义约束。 总结可能的调整方法: 1. 检查约束冲突,确保原材料和需求之间没有不可调和的矛盾。 2. 引入松弛变量,允许不满足某些约束,但惩罚目标函数。 3. 增加原材料采购选项,允许购买额外资源。 4. 分优先级处理订单,部分订单可以延迟或取消。 5. 使用两阶段方法,先检查可行性,再调整需求或资源。 然后需要具体说明如何在代码中实现这些调整,比如修改约束,添加变量,调整目标函数。例如,在代码中添加松弛变量,将原来的等式或不等式约束改为包含松弛的形式,并在目标中增加惩罚项,确保即使资源不足,问题仍有解,但成本增加。 同时,需要考虑如何确保这些调整后的模型在数学上是正确的,比如松弛变量的符号是否正确,惩罚系数是否合理,避免导致目标函数被过度影响。 最后,生成相关问题,可能涉及如何选择惩罚系数、如何处理多周期库存、如何检测不可行原因等。</think>### 线性规划问题可行性保障方法 要确保线性规划问题在给定原材料和订单需求下始终存在可行解,需从模型设计、约束条件调整和算法优化三方面入手。以下是具体方法及代码示例: --- #### 一、约束条件调整策略 1. **松弛不可行约束** 引入**松弛变量**$s_i \geq 0$,将硬约束转化为可容忍的软约束: $$ \sum_{j=1}^n a_{ij}x_j \leq b_i + s_i \quad (i=1,2,...,m) $$ 在目标函数中添加惩罚项,例如: $$ \min \ c^Tx + \lambda \sum_{i=1}^m s_i $$ ```python from scipy.optimize import linprog # 原约束系数矩阵A_ub和右侧b_ub # 添加松弛变量后的新约束矩阵 A_ub_new = np.hstack([A_ub, -np.eye(m)]) # s_i允许正偏差 b_ub_new = b_ub # 扩展目标函数系数(惩罚系数λ=10) c_new = np.concatenate([c, 10*np.ones(m)]) result = linprog(c_new, A_ub=A_ub_new, b_ub=b_ub_new) ``` 2. **动态资源扩展** 允许通过额外采购解决资源不足问题,添加采购变量$y_k$: $$ \sum_{j=1}^n a_{kj}x_j \leq b_k + y_k \quad (y_k \geq 0) $$ 目标函数中增加采购成本: $$ \min \ c^Tx + \sum_{k=1}^p d_ky_k $$ --- #### 二、模型设计优化 1. **两阶段可行性检查** ```python # 第一阶段:检查原始问题可行性 phase1_obj = np.zeros(n) phase1_A_ub = A_ub phase1_b_ub = b_ub phase1_result = linprog(phase1_obj, A_ub=phase1_A_ub, b_ub=phase1_b_ub) if not phase1_result.success: # 启动松弛策略或采购逻辑 print("启动动态资源补偿机制") ``` 2. **优先级分层约束** 将订单分为**必须满足**(刚性约束)和**可调节**(柔性约束)两类: $$ \begin{cases} \sum_{j=1}^n a_{rj}x_j = d_r & \text{(刚性需求)} \\ \sum_{j=1}^n a_{sj}x_j \geq 0.8d_s & \text{(柔性需求)} \end{cases} $$ --- #### 三、算法层面保障 1. **预处理消除矛盾约束** 使用矩阵秩分析检测线性相关约束: $$ \text{rank}([A|b]) = \text{rank}(A) $$ 若不等则存在矛盾约束[^3]。 2. **Barrier Method增强鲁棒性** 使用内点法处理大规模约束问题,通过障碍函数避免可行域边界的数值不稳定问题[^2]: ```python result = linprog(c, A_ub=A_ub, b_ub=b_ub, method='interior-point') ``` --- ### 关键数学验证 通过**Farkas引理**验证可行性:系统$Ax \leq b$有解当且仅当对任意$y \geq 0$满足$y^TA=0$时,有$y^Tb \geq 0$。若检测到不可行,可快速定位冲突约束[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值