【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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值