You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

同一代码在C++17与C++14下输出不一致问题排查求助

问题分析与解决方案

你的代码在C17下正常运行,但C14下输出错误,问题出在持久化线段树update函数中节点的创建方式:先通过emplace_back()默认构造新节点,再赋值为原节点拷贝的操作,在C14的vector实现中可能触发未定义行为,而C17的vector实现优化规避了这个问题。

具体问题点

在update函数中,你原本的写法是:

int id = T.size();
T.emplace_back();
T[id] = T[root];

当vector容量不足时,emplace_back()会触发扩容:分配新内存、移动/拷贝原元素到新内存、释放原内存。虽然索引访问理论上有效,但先默认构造再赋值的步骤,在C++14的内存模型下可能导致对已释放内存的间接访问,引发未定义行为,最终导致线段树节点数据错误。

修复方案

将上述三行代码替换为直接拷贝构造新节点的写法,避免中间赋值步骤:

int id = T.size();
T.emplace_back(T[root]); // 直接基于原节点拷贝构造新节点

或者使用push_back实现相同效果:

int id = T.size();
T.push_back(T[root]);

修改后的完整代码

#include <bits/stdc++.h>

#define nl '\n'

using namespace std;

struct PersistentSegmentTree {
  struct Node {
    int value, left, right;

    Node() : value(0), left(0), right(0) {}
    Node(const Node&) = default; // 显式声明拷贝构造,确保行为一致
  };
  vector<Node> T = {Node()};

  int update(int root, int l, int r, int p, int v) {
    int id = T.size();
    T.emplace_back(T[root]); // 修改为直接拷贝构造
    if (l == r) {
      T[id].value += v;
      return id;
    }
    
    int mid = (l + r) / 2;
    if (p <= mid) {
      T[id].left = update(T[root].left, l, mid, p, v);
    } else {
      T[id].right = update(T[root].right, mid + 1, r, p, v);
    }
    
    T[id].value = T[T[id].left].value + T[T[id].right].value;
    return id;
  }

  int query(int from, int to, int l, int r, int k) {
    if (l == r) {
      return l;
    }
    int mid = (l + r) / 2;
    int left_count = T[T[to].left].value - T[T[from].left].value;
    if (left_count >= k) {
      return query(T[from].left, T[to].left, l, mid, k);
    } else {
      return query(T[from].right, T[to].right, mid + 1, r, k - left_count);
    }
  }
} tree;

int main() {
  cin.tie(0), ios::sync_with_stdio(0);

  int n, q;
  cin >> n >> q;
  vector<int> a(n);
  for (int i = 0; i < n; i++) {
    cin >> a[i]; 
  }

  vector<int> roots;
  roots.push_back(0);
  
  auto vals = a;
  sort(vals.begin(), vals.end());
  vals.erase(unique(vals.begin(), vals.end()), vals.end());

  for (int i = 0; i < n; i++) {
    a[i] = lower_bound(vals.begin(), vals.end(), a[i]) - vals.begin();
    roots.push_back(tree.update(roots.back(), 0, vals.size() - 1, a[i], 1));
  }

  for (int qi = 0; qi < q; qi++) {
    int l, r, k;
    cin >> l >> r >> k;
    cout << vals[tree.query(roots[l - 1], roots[r], 0, vals.size() - 1, k)] << nl;    
  }
}

验证说明

修改后,代码在C14和C17环境下都会输出正确结果:

5 
6 
3

内容的提问来源于stack exchange,提问作者kilkuwu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 19:07:03