He said that listening to books on covetousness + data structure of practice, study a little.
Plain greedy
Consider all line segments to the right end point \ (b \) from small to large, consider each line segment requirements:
- If you already meet the requirements skip
- Otherwise, try to choose the number of rearward (right end point because after all segments of the right of this segment, so higher fault tolerance)
Therefore, we can build an array, \ (D [I] \) represents \ (I \) whether a number is selected (fill \ (1 \) or \ (0 \) ), sweep over \ ([l, r] \ ) interval sum, then the number can be put forward from the greedy.
For each segment requires \ (O (R & lt -. 1 + L) \) . Therefore, in the worst case \ (O (^ n-2) \) . But easy \ (52ms \) before.
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50005;
int n, d[N], c[N];
struct Seg{
int a, b, c;
bool operator < (const Seg &x) const {
return b < x.b;
}
}e[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
sort(e + 1, e + 1 + n);
int ans = 0;
for (int i = 1; i <= n; i++) {
int l = e[i].a, r = e[i].b, cnt = e[i].c;
for (int j = l; j <= r; j++)
cnt -= d[j];
if(cnt > 0) {
for (int j = r; j >= l && cnt; j--)
if(!d[j]) cnt--, ans++, d[j] = 1;
}
}
printf("%d\n", ans);
return 0;
}
optimization
Consider a data structure optimization.
We need to find three things:
- Inquiry \ ([l, r] \ ) digits interval
- The value \ (X \) position \ (1 + \)
- From back to front, front to find lower than the current position of a \ (0 \) position.
The first two is "intervals are summed, monotonous Edit", typical of Fenwick tree. $ O (nlog_250000) $
Third operation, may be disjoint-set optimization. Why can ensure that the time complexity of it? For each segment, at most only once to enumerate \ (1 \) (i.e., the start of that time), after each enumeration will enumerate \ (0 \) position, i.e., \ (d [i] = 0 \) , and then turn it into \ (1 \) , and then would not have access to the. And a total of \ (50,000 \) values, so complexity is \ (O (50000log_n) \)
\(33ms\)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 50001;
int n, d[N], c[N], f[N];
struct Seg{
int a, b, c;
bool operator < (const Seg &x) const {
return b < x.b;
}
}e[N];
// 树状数组
int inline ask(int x) {
int res = 0;
for (; x; x -= x & -x) res += c[x];
return res;
}
void inline add(int x) {
for (; x < N; x += x & -x) c[x]++;
}
// 并茶集:find(x) 表示找到 <= x 中最大的一个是 0 的数
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
int main() {
scanf("%d", &n);
for (int i = 0; i < N; i++) f[i] = i;
for (int i = 1; i <= n; i++)
scanf("%d%d%d", &e[i].a, &e[i].b, &e[i].c);
sort(e + 1, e + 1 + n);
int ans = 0;
for (int i = 1; i <= n; i++) {
int l = e[i].a, r = e[i].b, cnt = e[i].c;
// 取 [l, r] 选了多少个数
cnt -= ask(r) - ask(l - 1);
if(cnt > 0) {
for (int j = r; j >= l && cnt; ) {
// d[j] == 1 的情况每条线段至多出现一次
if(!d[j]) {
cnt--, ans++, d[j] = 1;
// j 被标记成 1 了,要指向 find(j - 1)
f[j] = j - 1;
// 维护树状数组
add(j);
};
if(find(j) != j) j = f[j];
else j--;
}
}
}
printf("%d\n", ans);
return 0;
}