洛谷P2070 离散化区间

洛谷P2070 离散化区间

『题目传送门』:P2070 刷墙
『思路1 题解原链接』

题目:

Farmer John已经设计了一种方法来装饰谷仓旁边的长栅栏(把栅栏认为是一根一维的线)。他把一只画刷绑在他最喜爱的奶牛Bessie身上,之后就去喝一杯冰水,而Bessie隔着栅栏来回走,当她走过某个地方,这里的一段栅栏就被刷上了涂料。

Bessie从栅栏上的位置0开始,并且遵循着一个N次移动的次序(1 <= N <= 100,000)。例如“10 L”表示Bessie向左移动了10个单位长度,“15 R”表示Bessie向右移动了15个单位长度。现给出Bessie所有移动的列表,Farmer John想要知道哪些区域的栅栏至少涂了两层涂料(只涂一层涂料的区域可能在大雨中被洗掉)。Bessie在她的行走中最远到达距起始点1,000,000,000个单位长度。

题目大意:给定(n)个区间([l,r])其中区间的长度(1leq l,rleq 1e^9),要求给出区间相交数大于2的区间长度。
例如模拟给定的样例:

6
2 R
6 L
1 R
8 L
1 R
2 R

思路1 思维题:分情况考虑离散化(对区间考虑)

说实话这个有点难弄清,需要细细的思考。

思路:从小到达扫描区间,维护一个可能发生重叠的区间【L,R】,在当前维护的区间的右边寻找一个区间【a[i].l,a[i].r】
使得当前的可能区间更新:
分为两种情况:(对应到图右上角的1和2)

  1. 查询的区间的右端点是小于当前维护区间的右端点的,那么把【当前维护区间的左端点,当前查询区间的右端点】这一区间加入到重复区间长度计算中。并且试图在【当前查询区间的右端点,当前维护区间的右端点】寻找下一个可能的重复区间。
  2. 查询的区间的右端点是大于当前维护区间的右端点的,那么把【当前维护区间的左端点,当前维护区间的右端点】这一区间加入到重复区间长度计算中。并且试图在【当前维护区间的右端点,当前查询区间的右端点】寻找下一个可能的重复区间
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
string dir;
ll l, r;
vector<pair<ll, ll>> alls;
ll res;
int main() {
  cin >> n;
  for (int i = 0; i < n; i++) {
    cin >> r >> dir;
    if (dir == "R")
      r = l + r;
    else
      r = l - r;
    ll tl, tr;
    tl = min(l, r), tr = max(l, r);
    alls.push_back(make_pair(tl, tr));
    l = r;
  }
  sort(alls.begin(), alls.end());
  //找它区间后面左端点小于右端点并且右端点最右边的点
  ll lft = alls[0].first;
  ll rgt = alls[0].second;
  for (int i = 1; i < n; i++) {
    if (alls[i].second > lft) {  //至少是有意义的区间(可能的区间至少是大于1的)
      //缩小左边的区间
      alls[i].first = max(alls[i].first, lft);
      //【alls[i].first,alls[i].second】
      //【lft          ,rgt           】
      if (alls[i].second > rgt) {    //比之前的右端点大全部覆盖
        res += rgt - alls[i].first;  //求公共区间
        lft = rgt;  //接下来有可能的区间是[rgt,alls[i].second]
        rgt = alls[i].second;
      } else {                                  //比之前的右端点小
        res += alls[i].second - alls[i].first;  //求公共区间
        lft = alls[i].second;  //接下来可能的区间是[alls.second,rgt]
      }
    }
  }
  cout << res << endl;

  return 0;
}

思路2:离散化+差分(对点考虑)

对于一个区间【L,R】每出现一次,就相当于在a[l]+=1,a[r+1]-=1;
那么就显而易见的,直接对区间的左右端点左修改,(m)次查询即完成了相应的修改,最后从小到大扫描所有的区间,若进入区间左端点后当前的差分值从1变为了2即开始进入到多个区间重合的区域,从2变到1即退出重合的区域。如下图

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
static int faster_iostream = []() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(NULL);
  return 0;
}();
int n;
string dir;
ll l, r;
vector<pii> point;
ll res;
int main() {
  cin >> n;
  for (int i = 0; i < n; i++) {
    cin >> r >> dir;
    if (dir == "R")
      r = l + r;
    else
      r = l - r;
    ll tl, tr;
    tl = min(l, r), tr = max(l, r);
    point.push_back(make_pair(tl, 1));
    point.push_back(make_pair(tr, -1));
    l = r;
  }
  sort(point.begin(), point.end());
  int old = 0;
  int sum = 0;

  for (int i = 0; i < point.size(); i++) {
    old = sum;
    sum += point[i].second;
    if (old == 1 && sum == 2) l = point[i].first;
    if (old == 2 && sum == 1) res += point[i].first - l;
  }
  cout << res << endl;
  return 0;
}
原文地址:https://www.cnblogs.com/adameta/p/12420069.html