【蓝桥】奇怪的线段

一、题目

1、题目描述

在一维数轴上,小蓝画了 n n n 个闭区间线段,小桥会多次询问你,每次给定两个点 a , b a, b a,b,问有多少个区间包含 a a a 点,但是不包含 b b b 点。

输入格式

第一行输入两个整数 n , q n, q n,q, n n n 代表区间个数, q q q 代表询问个数。

接下来 n n n 行,每行两个整数 l i , r i l_i, r_i li,ri,代表一个左右端点为 l i , r i l_i, r_i li,ri 的闭区间。

接下来 q q q 行,每行两个整数 a i , b i a_i, b_i ai,bi,代表询问存在多少个区间,包含 a i a_i ai 点,但不包含 b i b_i bi 点。

输出格式

输出 q q q,第 i i i 行代表第 i i i 个询问的答案。

样例输入

4 3
1 3
2 5
3 7 
4 8
1 5
2 9
5 1

样例输出

1
2
3

评测数据范围

  • 1 ≤ n ≤ 2 × 1 0 5 1 \le n \le 2 \times 10^5 1≤n≤2×105
  • 1 ≤ q ≤ 2 × 1 0 5 1 \le q \le 2 \times 10^5 1≤q≤2×105
  • 1 ≤ l i < r i ≤ 2 × 1 0 5 1 \le l_i \lt r_i \le 2 \times 10^5 1≤li<ri≤2×105
  • 1 ≤ a i , b i ≤ 2 × 1 0 5 1 \le a_i,b_i \le 2 \times 10^5 1≤ai,bi≤2×105

2、基础框架

cpp 复制代码
#include <iostream>
using namespace std;
int main()
{
  // 请在此输入您的代码
  return 0;
}

3、原题链接

奇怪的线段

二、解题报告

1、思路分析

先思考弱化版本:如果只有一个点,即仅仅考虑覆盖 a a a 点的区间有多少个。

考虑离线算法,我们对于每一个区间的左端点维护一个 vector 容器,装下对应的右端点,同时维护一个全局的线段树,从左向右扫描,扫描到一个区间的左端点时,将对应的右端点加入到线段树中,当扫描到一个右端点时,将右端点从线段树中移除,到扫描到一个 a a a 时,查询线段树中节点数量,即为答案。

那么此题相似,只不过查询的不是全局区间和,而是局部区间和

具体如下,总共分为三种情况:

  1. 如果 a = = b a == b a==b ,答案为 0 0 0。

  2. 如果 a < b a \lt b a<b ,对区间按照左区间排序,从左向右扫描,并且维护一个区间和线段树,扫描到一个区间的左端点时,将对应的右端点加入到线段树中,当扫描到一个右端点时,将右端点从线段树中移除,到扫描到一个 a a a 时,查询线段树中 [ a , b ) [a, b) [a,b) 的区间和,即为答案。

  3. 如果 a > b a \gt b a>b ,对区间按照右区间排序,从右向左扫描,并且维护一个区间和线段树,扫描到一个区间的右端点时,将对应的左端点加入到线段树中,当扫描到一个左端点时,将左端点从线段树中移除,到扫描到一个 a a a 时,查询线段树中 ( b , a ] (b,a] (b,a] 的区间和,即为答案。

实际上,需要维护的是动态区间和,代码中用树状数组来代替维护区间和。

相关知识点:

2、时间复杂度

O ( l o g N ) O(logN) O(logN)

3、代码详解

  • 暴力:超时
cpp 复制代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n;
    int q;
    cin >> n >> q;

    vector<vector<int> > ranges(n, vector<int>(2));
    
    for (int i = 0; i < n; i++) {
        cin >> ranges[i][0];
        cin >> ranges[i][1];
    }

    sort(ranges.begin(), ranges.end());

    int a, b;
    for (int i = 0; i < q; i++) {
        cin >> a >> b;
        
        if (a == b) {
            cout << "0" << endl;
            continue;
        }

        int cnt = 0;

        for (vector<int> &range : ranges) {
            int l = range[0];
            int r = range[1];

            if ((a < l || a > r) || (l <= b && b <= r)) {
                continue;
            }
            cnt++;
        }
        cout << cnt << endl;
    }
    return 0;
}
  • 树状数组
cpp 复制代码
#include <iostream>
#include <vector>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int N = 2e5 + 100;

vector<int> posL[N], posR[N];
int n, q;
vector<pair<int, int> > query[N]; //要查询的信息
int ans[N];

//获取最右侧的1
#define lowbit(x) ((x) & (-(x)))

int tree[N];

//1~x范围的前缀和
int get_sum(int x) {
    int sum = 0;
    while (x) {
        sum += tree[x];
        x -= lowbit(x);
    }
    return sum;
}

//单点更新------pos位置更新:+val
void update(int pos, int val) {
    while (pos < N) {
        tree[pos] += val;
        pos += lowbit(pos); //受影响的位置
    }
}

//从左向右扫描
void L_to_R() {

    memset(tree, 0, sizeof(tree));

    for (int i = 1; i < N; ++i) {
        for (int r : posL[i]) { //范围的右端点
            update(r, 1);
        }

        for (auto [b, id] : query[i]) {
            if (b > i) {
                ans[id] = get_sum(b - 1);
            }
        }

        update(i, -posR[i].size());
    }
}

//从右向左扫描
void R_to_L() {

    memset(tree, 0, sizeof(tree));

    for (int i = N - 1; i > 0; --i) {
        for (int l : posR[i]) { //范围的左端点
            update(l, 1);
        }
        
        for (auto [b, id] : query[i]) {
            if (b < i) {
                ans[id] = get_sum(N - 1) - get_sum(b);
            }
        }
        update(i, -posL[i].size());
    }
}

void sol() {
    int l, r, a, b;
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) {
        cin >> l >> r;
        posL[l].push_back(r);
        posR[r].push_back(l);
    }    
    
    for (int i = 1; i <= q; ++i) {
        cin >> a >> b;
        query[a].push_back({b, i});
    }

    L_to_R();
    R_to_L();

    for (int i = 1; i <= q; ++i) {
        cout << ans[i] << '\n';
    }
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int T = 1;
    while (T--) {
        sol();
    }
    exit(0);
}
相关推荐
摆烂小白敲代码2 个月前
详解树状数组(C/C++)
c语言·数据结构·c++·算法·树状数组·bit·fenwick tree
2302_807070712 个月前
河南萌新联赛2024第(六)场:郑州大学(补题ABCDFGIL)
c++·二分·树状数组·萌新联赛
xhchen20235 个月前
第 402 场 LeetCode 周赛题解
leetcode·前缀和·动态规划·哈希·计数·树状数组
无名之逆5 个月前
3072. 将元素分配到两个数组中 II Rust 线段树 + 离散化
开发语言·算法·rust·线段树·二分·树状数组·离散化
小哈里7 个月前
【笔试】美团2024年春招第二场笔试(技术)
算法·前缀和·笔试·模拟·树状数组
YMWM_8 个月前
acwing算法提高之数据结构--树状数组
数据结构·算法·树状数组
EQUINOX18 个月前
CDQ分治详解,一维、二维、三维偏序
数据结构·c++·算法·排序算法·分治·树状数组
Wy. Lsy10 个月前
小红统计区间(hard) - 树状数组 + 离散化
c++·算法·树状数组·离散化
ykycode1 年前
【数据结构】树状数组总结
数据结构·算法与数据结构·树状数组
切勿踌躇不前1 年前
ACwing算法备战蓝桥杯——Day30——树状数组
c++·算法·蓝桥杯·树状数组