AcWing 4195. 線段覆蓋(離散化+差分)
題目描述
在一個坐標軸上有 \(n\) 條線段。
每條線段的每個端點的坐標都為整數。
可能存在退化成點的線段。
線段之間可以相互交叉、嵌套甚至重合。
請你計算,對於每個 \(k \in\{1,2, \ldots, n\}\) ,坐標軸中共有多少個整數坐標的點滿足恰好被 \(k\) 條線段覆蓋。
注意,左右端點分別為 \(l_{i}, r_{i}\) 的線段覆蓋點 \(x\) 當且僅當 \(l_{i} \leq x \leq r_{i}\) 。
輸入格式
第一行包含整數 \(n\)。
接下來 \(n\) 行,每行包含兩個整數 \(l\_i,r\_i\),表示一條線段的左右端點。
輸出格式
一行 \(n\) 個整數,其中第 \(i\) 個整數表示坐標軸中滿足恰好被 \(i\) 條線段覆蓋的整數坐標的點的數量。
數據范圍
前三個測試點滿足 \(1 \leq n \leq 3\) 。
所有測試點滿足 \(1 \leq n \leq 2 \times 10^{5}, 0 \leq l_{i} \leq r_{i} \leq 10^{18}\) 。
輸入樣例1:
3
0 3
1 3
3 8
輸出樣例1:
6 2 1
輸入樣例2:
3
1 3
2 4
5 7
輸出樣例2:
5 2 0
算法
差分數組,對於第一個樣例:
朴素做法如上,對於所有的點差分求前綴和即可,但是發現只要保存左右端點消息即可,端點和端點之間的點被線段覆蓋的條數一定是一樣的。
map做法:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long LL;
const int N = 200010;
map<LL, int> b;
LL ans[N];
int main()
{
int n;
scanf("%d", &n);
for (int i = 0; i < n; i ++ )
{
LL l, r;
scanf("%lld%lld", &l, &r);
b[l] ++, b[r + 1] -- ;
}
LL sum = 0, last = -1;
for (auto& [k, v]: b)
{
if (last != -1) ans[sum] += k - last;
sum += v;
last = k;
}
for (int i = 1; i <= n; i ++ )
printf("%lld ", ans[i]);
return 0;
}
離散化后差分:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1000010;
LL l[N], r[N], res[N], x[N];
vector<LL> all;
int n, m;
int main()
{
scanf("%d", &n);
for(int i = 0; i < n; i++)
{
scanf("%lld%lld", &l[i], &r[i]);
r[i]++;
all.push_back(l[i]);
all.push_back(r[i]);
}
sort(all.begin(), all.end());
all.erase(unique(all.begin(), all.end()), all.end());
m = all.size();
for(int i = 0 ; i < n; i++)
{
int pos1 = lower_bound(all.begin(), all.end(), l[i]) - all.begin();
int pos2 = lower_bound(all.begin(), all.end(), r[i]) - all.begin();
x[pos1]++;
x[pos2]--;
}
LL sum = 0;
for(int i = 0; i < m; i++){
if(i != 0){
res[sum] += all[i] - all[i - 1];
}
sum += x[i];
}
for(int i = 1; i <= n; i++)
cout<<res[i]<<" ";
return 0;
}