区间求差

本文介绍了一种算法,用于解决两个区间集合的差集问题,并通过一个具体例子进行了说明。该算法首先将区间进行合并并排序,然后计算A集合去掉B集合后的剩余长度。

本体来自 hihocoder #1305

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

给定两个区间集合 A 和 B,其中集合 A 包含 N 个区间[ A1A2 ], [ A3A4 ], ..., [ A2N-1A2N ],集合 B 包含 M 个区间[ B1B2 ], [ B3B4 ], ..., [ B2M-1B2M ]。求 A - B 的长度。

例如对于 A = {[2, 5], [4, 10], [14, 18]}, B = {[1, 3], [8, 15]}, A - B = {(3, 8), (15, 18]},长度为8。

输入

第一行:包含两个整数 N 和 M (1 ≤ NM ≤ 100000)。

第二行:包含 2N 个整数 A1A2, ..., A2N (1 ≤ Ai ≤ 100000000)。

第三行:包含 2M 个整数 B1B2, ..., B2M (1 ≤= Bi ≤ 100000000)。

输出

一个整数,代表 A - B 的长度。

样例输入

3 2
2 5 4 10 14 18
1 3 8 15

样例输出

8

分析

这道题的思路并不复杂,很容易想到,我们可以先对A、B中的区间进行合并,得到一些彼此不交的区间,这些区间按位置排序,然后对于A中每个区间,考察所有覆盖住该区间的B中区间,则该区间对A-B的贡献就是这些B中区间之间的缝隙。显然,这样做我们得到的算法的复杂度是O(NlogN)。

代码如下:

#include<iostream>
#include<vector>
#include<deque>
#include<stack>
#include<string>
#include<algorithm>
#include<string.h>
#include<sstream>

using namespace std;



struct Interval {
    int start;
    int end;
    Interval() :start(0), end(0) {}
    Interval(int a, int b) :start(a), end(b) {}
};

const int max_n = 100000;
Interval a[max_n];
Interval b[max_n];


bool in_cmp(const Interval& a, const Interval& b){
    return a.start < b.start;
}

bool is_overlap(Interval& a, Interval& b){
    if (a.end < b.start) return false;
    if (b.end < a.start) return false;

    return true;
}

Interval my_union(Interval& a, Interval& b){
    int start = min(a.start, b.start);
    int end = max(a.end, b.end);

    Interval u(start, end);
    return u;
}

void union_in(int N, int M, vector<Interval>& a_u, vector<Interval>& b_u) {
    sort(a, a + N, in_cmp);
    int i = 0;
    while (i < N) {
        Interval now_in = a[i];
        int j = i + 1;
        while (j < N){
            if (is_overlap(a[j], now_in)) {
                now_in = my_union(now_in, a[j]);
                ++j;
            }
            else break;
        }
        a_u.push_back(now_in);
        i = j;
    }

    sort(b, b + M, in_cmp);
    i = 0;
    while (i < M) {
        Interval now_in = b[i];
        int j = i + 1;
        while (j < M){
            if (is_overlap(b[j], now_in)) {
                now_in = my_union(now_in, b[j]);
                ++j;
            }
            else break;
        }
        b_u.push_back(now_in);
        i = j;
    }
}

int sol(vector<Interval> &a_u, vector<Interval> &b_u) {
    int res = 0;

    auto it_a = a_u.begin();
    auto it_b = b_u.begin();
    while (it_a != a_u.end()) {
        if (it_b == b_u.end() || (it_b->end >= it_a->start && !is_overlap(*it_b, *it_a))) {
            res += it_a->end - it_a->start;
            ++it_a;
        }
        else if (it_b->end < it_a->start) {
            ++it_b;
        }
        else {
            if (it_b->start > it_a->start) res += it_b->start - it_a->start;


            auto it_be = it_b + 1;
            while (it_be != b_u.end() && is_overlap(*it_be, *it_a)) {
                res += it_be->start - (it_be - 1)->end;
                ++it_be;
            }
            --it_be;
            if (it_be->end < it_a->end) res += it_a->end - it_be->end;
            ++it_a;
            it_b = it_be;
        }
    }

    return res;

}

int main() {


    int N, M;

    cin >> N >> M;
    for (int i = 0; i < N; ++i) {
        int x, y;
        cin >> x >> y;
        a[i].start = x;
        a[i].end = y;
    }
    for (int i = 0; i < M; ++i) {
        int x, y;
        cin >> x >> y;
        b[i].start = x;
        b[i].end = y;
    }

    vector<Interval> a_u;
    vector<Interval> b_u;
    union_in(N, M, a_u, b_u);

    int res = sol(a_u, b_u);

    cout << res << endl;

    //system("pause");

    return 0;
}

 

转载于:https://www.cnblogs.com/mhkds/p/6937183.html

在计算区间最大公约数(GCD)时,通常指的是在某个数值区间内找到两个或多个数的最大公约数。最大公约数(GCD)是能够同时整除这些数的最大整数。以下是几种常见的算法和方法,用于计算最大公约数: ### 欧几里得算法(辗转相除法) 欧几里得算法是一种经典的计算两个数的最大公约数的方法。其基本思想是:对于两个数 $ p $ 和 $ q $,如果 $ q = 0 $,则最大公约数是 $ p $;否则,用 $ p $ 除以 $ q $ 得到余数 $ r $,然后用 $ q $ 和 $ r $ 继续这个过程,直到余数为0。此时的除数即为最大公约数。 Java 实现代码如下: ```java public class EuclideanAlgorithm { public static int gcd(int p, int q) { while (q != 0) { int r = p % q; p = q; q = r; } return p; } public static void main(String[] args) { int a = 48, b = 18; System.out.println("GCD of " + a + " and " + b + " is " + gcd(a, b)); } } ``` ### 连续整数法 连续整数法是一种较为直观但效率较低的方法。其基本思想是从较小的数开始,逐个递减,直到找到一个能同时整除两个数的数。 Java 实现代码如下: ```java public class ConsecutiveIntegerGCD { public static int gcd(int x, int y) { int min = Math.min(x, y); while (x % min != 0 || y % min != 0) { min--; } return min; } public static void main(String[] args) { int a = 48, b = 18; System.out.println("GCD of " + a + " and " + b + " is " + gcd(a, b)); } } ``` ### 更相减损术 更相减损术是一种古老的算法,适用于任何需要求最大公约数的场合。其基本思想是:用较大的数减去较小的数,然后用所得的差和较小的数继续这个过程,直到差和较小的数相等为止。 Java 实现代码如下: ```java public class SubtractionGCD { public static int gcd(int x, int y) { while (x != y) { if (x > y) { x -= y; } else { y -= x; } } return x; } public static void main(String[] args) { int a = 48, b = 18; System.out.println("GCD of " + a + " and " + b + " is " + gcd(a, b)); } } ``` ### Stein 算法(二进制 GCD 算法) Stein 算法是一种基于二进制运算的高效算法,特别适用于大整数的情况。其基本思想是利用位移和减法操作来替代传统的除法和取余操作,从而提高计算效率。 Java 实现代码如下: ```java public class SteinGCD { public static int gcd(int u, int v) { if (u == 0) return v; if (v == 0) return u; int shift; for (shift = 0; ((u | v) & 1) == 0; shift++) { u >>= 1; v >>= 1; } while ((u & 1) == 0) u >>= 1; do { while ((v & 1) == 0) v >>= 1; if (u > v) { int temp = u; u = v; v = temp; } v = v - u; } while (v != 0); return u << shift; } public static void main(String[] args) { int a = 48, b = 18; System.out.println("GCD of " + a + " and " + b + " is " + gcd(a, b)); } } ``` ### 区间最大公约数的应用 在实际应用中,区间最大公约数的概念可以扩展到多个数的情况。例如,在给定的区间内找到一组数的最大公约数,可以通过依次计算这些数的 GCD 来实现。具体来说,可以先计算前两个数的 GCD,然后将结果与第三个数计算 GCD,依此类推,直到所有数都被处理完毕。 Java 实现代码如下: ```java public class MultipleGCD { public static int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } public static int gcd(int[] numbers) { int result = numbers[0]; for (int i = 1; i < numbers.length; i++) { result = gcd(result, numbers[i]); } return result; } public static void main(String[] args) { int[] numbers = {48, 18, 30}; System.out.println("GCD of the array is " + gcd(numbers)); } } ``` ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值