当前位置:首页 > 技术 > 正文内容

线段树在区间操作与连通块查询中的应用

访客 技术 2026年7月25日 1

区间加与异或和查询实现

问题需求:支持区间加法操作,查询区间内元素异或和的后m位(m≤10)。序列长度1e5,操作数1e5。

解决方案:采用线段树结合bitset优化。建立1024位bitset存储区间内数值分布的奇偶性,区间加法通过循环位移实现。当区间长度小于128时切换为暴力计算以优化常数。

const int BIT_RANGE = 1024;
const int BLOCK_SIZE = 128;

struct SegmentNode {
    bitset<BIT_RANGE> bit_data;
    int shift_tag = 0;
};

SegmentNode tree[4 * MAXN];
int base_arr[MAXN];

void merge_nodes(int parent) {
    tree[parent].bit_data = tree[left_child(parent)] ^ tree[right_child(parent)];
}

void apply_shift(int node, int shift_val) {
    shift_val &= BIT_RANGE - 1;
    tree[node].bit_data = (tree[node].bit_data << shift_val) | 
                          (tree[node].bit_data >> (BIT_RANGE - shift_val));
    tree[node].shift_tag += shift_val;
}

void propagate(int node) {
    if (!tree[node].shift_tag) return;
    apply_shift(left_child(node), tree[node].shift_tag);
    apply_shift(right_child(node), tree[node].shift_tag);
    tree[node].shift_tag = 0;
}

void build_tree(int node, int left, int right) {
    if (right - left + 1 <= BLOCK_SIZE) {
        for (int idx = left; idx <= right; ++idx)
            tree[node].bit_data.flip(base_arr[idx]);
        return;
    }
    int mid = (left + right) >> 1;
    build_tree(left_child(node), left, mid);
    build_tree(right_child(node), mid + 1, right);
    merge_nodes(node);
}

void update_range(int node, int left, int right, int start, int end, int add_val) {
    if (right - left + 1 <= BLOCK_SIZE) {
        int low = max(left, start);
        int high = min(right, end);
        for (int idx = low; idx <= high; ++idx) 
            tree[node].bit_data.flip((base_arr[idx] + tree[node].shift_tag) & (BIT_RANGE-1));
        for (int idx = low; idx <= high; ++idx) 
            base_arr[idx] += add_val;
        for (int idx = low; idx <= high; ++idx) 
            tree[node].bit_data.flip((base_arr[idx] + tree[node].shift_tag) & (BIT_RANGE-1));
        return;
    }
    if (left >= start && right <= end) 
        return apply_shift(node, add_val);
    propagate(node);
    int mid = (left + right) >> 1;
    if (start <= mid) update_range(left_child(node), left, mid, start, end, add_val);
    if (end > mid) update_range(right_child(node), mid + 1, right, start, end, add_val);
    merge_nodes(node);
}

bitset<BIT_RANGE> query_range(int node, int left, int right, int start, int end) {
    bitset<BIT_RANGE> result;
    if (right - left + 1 <= BLOCK_SIZE) {
        int low = max(left, start);
        int high = min(right, end);
        for (int idx = low; idx <= high; ++idx)
            result.flip((base_arr[idx] + tree[node].shift_tag) & (BIT_RANGE-1));
        return result;
    }
    if (left >= start && right <= end)
        return tree[node].bit_data;
    propagate(node);
    int mid = (left + right) >> 1;
    if (start <= mid) result ^= query_range(left_child(node), left, mid, start, end);
    if (end > mid) result ^= query_range(right_child(node), mid + 1, right, start, end);
    return result;
}

区间连通块数量查询实现

问题需求:维护动态集合,支持插入/删除操作,查询包含指定元素的连通块大小(相邻元素差值≤k视为连通)。

解决方案:离散化后建立线段树,节点记录区间左右边界、元素总数和连通块数量。查询时通过二分确定连通块边界。

struct SegmentInfo {
    long min_val, max_val;
    int element_count, component_count;
};

SegmentInfo combine_segments(const SegmentInfo &a, const SegmentInfo &b) {
    if (a.element_count == 0) return b;
    if (b.element_count == 0) return a;
    long new_min = min(a.min_val, b.min_val);
    long new_max = max(a.max_val, b.max_val);
    int total_elements = a.element_count + b.element_count;
    int merge_adjust = (max(a.min_val, b.min_val) - min(a.max_val, b.max_val) <= k) ? 1 : 0;
    return { new_min, new_max, total_elements, 
             a.component_count + b.component_count - merge_adjust };
}

void update_tree(int node, int left, int right, int pos, int delta) {
    if (left == right) {
        tree[node].min_val = tree[node].max_val = values[pos];
        tree[node].element_count += delta;
        tree[node].component_count += delta;
        return;
    }
    int mid = (left + right) >> 1;
    if (pos <= mid) update_tree(left_child(node), left, mid, pos, delta);
    else update_tree(right_child(node), mid + 1, right, pos, delta);
    tree[node] = combine_segments(tree[left_child(node)], tree[right_child(node)]);
}

SegmentInfo query_segment(int node, int left, int right, int ql, int qr) {
    if (ql <= left && qr >= right) return tree[node];
    int mid = (left + right) >> 1;
    if (qr <= mid) return query_segment(left_child(node), left, mid, ql, qr);
    if (ql > mid) return query_segment(right_child(node), mid + 1, right, ql, qr);
    return combine_segments(
        query_segment(left_child(node), left, mid, ql, qr),
        query_segment(right_child(node), mid + 1, right, ql, qr)
    );
}

int find_left_bound(int pos) {
    int low = 1, high = pos;
    while (low <= high) {
        int mid = (low + high) >> 1;
        SegmentInfo seg = query_segment(1, 1, n, mid, pos);
        if (seg.component_count == 1) {
            high = mid - 1;
            count_cache = seg.element_count;
        } else low = mid + 1;
    }
    return count_cache;
}

int find_right_bound(int pos) {
    int low = pos, high = n;
    while (low <= high) {
        int mid = (low + high) >> 1;
        SegmentInfo seg = query_segment(1, 1, n, pos, mid);
        if (seg.component_count == 1) {
            low = mid + 1;
            count_cache = seg.element_count;
        } else high = mid - 1;
    }
    return count_cache;
}

int main() {
    // 离散化处理
    vector<long> all_values;
    for (int i = 0; i < m; ++i) {
        cin >> ops[i] >> data[i];
        all_values.push_back(data[i]);
    }
    sort(all_values.begin(), all_values.end());
    all_values.erase(unique(all_values.begin(), all_values.end()), all_values.end());
    
    // 建立线段树
    for (int i = 0; i < m; ++i) {
        int pos = lower_bound(all_values.begin(), all_values.end(), data[i]) - all_values.begin() + 1;
        if (ops[i] == INSERT) {
            state[pos] = !state[pos];
            update_tree(1, 1, n, pos, state[pos] ? 1 : -1);
        } else {
            int right_part = find_right_bound(pos);
            int left_part = find_left_bound(pos);
            cout << (left_part + right_part - 1) << endl;
        }
    }
}
标签: 线段树bitset

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。