【CF997E】Good Subsegments (线段树+单调栈)

本文详细解析了CF997E题目的算法思路,通过维护区间最小值和最小值个数,利用线段树解决排列中好的子区间查询问题。采用离线询问策略,结合单调栈优化,实现O(nlogn)的时间复杂度。

Description

原题链接
给你一个长度为\(n\)的排列\(~P\),定义一段子区间是好的,当且仅当这个子区间内的值构成了连续的一段。例如对于排列\(\{1,3,2 \}\)\([1, 1], [2, 2], [3, 3], [2, 3], [1, 3]\)是好的区间。
\(q\)次询问,每次询问\(L,R\), 求有多少\(L \leq l \leq r \leq R\),满足\([l, r]\)是好的区间。\(1 \leq n, q \leq 1.2 \times 10 ^ 5\).

Solution

可以发现,区间\([l, r]\)是好的,当且仅当\(~(Max_{i = l}^{r} - Min_{i = l} ^ {r}) - (r - l) = 0\).
考虑维护一段区间上式的最小值(以下的最小值都指上式最小值),每一个最小值为\(0\)的位置都可以和当前的\(r\)组成一个好的区间。不难发现,一个右端点能产生的贡献为它左边的点的最小值为\(0\)的个数,于是可以在线段树上维护最小值和最小值个数,以及每个最小值为\(0\)的位置产生的贡献。
由于右边新加的点会对已有的点产生影响,考虑离线询问,按右端点排序,用两个单调栈分别维护当前\(Min\)\(Max\)。右端点右移时,势必会使整个区间的最小值减一,也势必会使其未右移前的右端点对答案产生一轮贡献,每次处理右端点等于当前枚举点的答案。时间复杂度\(O(n \log n)\).

Code

#include <bits/stdc++.h>

#define For(i, j, k) for (int i = j; i <= k; ++ i)
#define Forr(i, j, k) for (int i = j; i >= k; -- i)

using namespace std;

typedef long long ll;

inline int read() {
    int x = 0, p = 1; char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') p = -1;
    for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * p;
}

inline void File() {
    freopen("CF997E.in", "r", stdin);
    freopen("CF997E.out", "w", stdout);
}

const int N = 1.2e5 + 10;
int n, q, a[N], s1[N], t1, s2[N], t2, tt = 1; ll ans[N];

struct Query {
    int id, l, r;
    bool operator < (const Query &rhs) const { return r < rhs.r; }
} Q[N];

namespace Segment_Tree {
#define lc (rt << 1)
#define rc (rt << 1 | 1)
#define mid (l + r >> 1)

    const int MAXN = N << 2;
    int mn[MAXN], tag[MAXN], t[MAXN]; ll sum[MAXN], tg[MAXN];
    
    inline void pushdown(int rt) {
        if (tag[rt]) {
            mn[lc] += tag[rt], mn[rc] += tag[rt];
            tag[lc] += tag[rt], tag[rc] += tag[rt];
            tag[rt] = 0;
        }

        if (tg[rt]) {
            if (mn[lc] == mn[rt]) sum[lc] += 1ll * t[lc] * tg[rt], tg[lc] += tg[rt];
            if (mn[rc] == mn[rt]) sum[rc] += 1ll * t[rc] * tg[rt], tg[rc] += tg[rt];
            tg[rt] = 0;
        }
    }

    inline void pushup(int rt) {
        t[rt] = 0, mn[rt] = min(mn[lc], mn[rc]);
        
        t[rt] = mn[rt] == mn[lc] ? t[rt] + t[lc] : t[rt];
        t[rt] = mn[rt] == mn[rc] ? t[rt] + t[rc] : t[rt];

        sum[rt] = sum[lc] + sum[rc];
    }

    inline void Build(int rt, int l, int r) {
        mn[rt] = l, t[rt] = 1;
        if (l ^ r) Build(lc, l, mid), Build(rc, mid + 1, r);
    }

    inline void update(int rt, int l, int r, int L, int R, int v) {
        if (L <= l && r <= R) { mn[rt] += v, tag[rt] += v; return ; }
        pushdown(rt); if (L <= mid) update(lc, l, mid, L, R, v);
        if (R > mid) update(rc, mid + 1, r, L, R, v); pushup(rt);
    }   

    inline ll query(int rt, int l, int r, int L, int R) {
        if (L <= l && r <= R) return sum[rt]; pushdown(rt);
        if (R <= mid) return query(lc, l, mid, L, R);
        if (L > mid) return query(rc, mid + 1, r, L, R);
        return query(lc, l, mid, L, R) + query(rc, mid + 1, r, L, R);
    }

#undef lc
#undef rc
#undef mid
}

int main() {
    File();

    using namespace Segment_Tree;

    n = read(); For(i, 1, n) a[i] = read();
    q = read(); For(i, 1, q) Q[i].l = read(), Q[i].r = read(), Q[i].id = i;

    sort(Q + 1, Q + 1 + q);

    Build(1, 1, n);

    For(nr, 1, n) {

        mn[1] -= 1, tag[1] -= 1;

        for (; t1 && a[s1[t1]] < a[nr]; -- t1)
            update(1, 1, n, s1[t1 - 1] + 1, s1[t1], a[nr] - a[s1[t1]]);
        s1[++ t1] = nr;
        
        for (; t2 && a[s2[t2]] > a[nr]; -- t2)
            update(1, 1, n, s2[t2 - 1] + 1, s2[t2], a[s2[t2]] - a[nr]);
        s2[++ t2] = nr;

        sum[1] += t[1], tg[1] += 1;

        for (; tt <= q && Q[tt].r == nr; ++ tt)
            ans[Q[tt].id] = query(1, 1, n, Q[tt].l, nr);
    }

    For(i, 1, q) printf("%lld\n", ans[i]);

    return 0;
}

转载于:https://www.cnblogs.com/LSTete/p/9757600.html

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、付费专栏及课程。

余额充值