一、题目大意
给我们一些闭区间[ai , bi],其中 1 <= ai <= bi <= 50000,让我们求出一个集合,使得这个集合与 区间 [ai , bi]有 ci个共同元素,对于所有的 1<=i <=n个区间而言。
二、解题思路
根据题目范围,我们可以对 1到50000的数字进行循环,然后每次循环中用线段树进行操作,O(n*logn)的复杂性时间上可行。
1、我们可以把所有的区间按照区间的起点进行排序,然后计算出每个区间的 bi - ci + 1,用线段树维护各个区间的 bi - ci + 1的最小值。
2、针对每次循环的数字 num,只需要用二分确定 ai 小于等于它的范围 R,然后从线段树中查询出其中最小的 bi - ci + 1,如果 bi - ci + 1 == num,代表这个数字需要被选择,答案加一,同时需要更新线段树上的[0 , R) 内所有的 bi - ci + 1 值为原来的值 + 1
3、每次循环 num后,判断 num 是否等于某个区间的 bi,如果等于,则将对应的 bi 在线段树上的节点的 bi - ci + 1 设置为50007(50000以上则为废弃节点,因为大于所有的num),然后循环向上更新父节点即可。
本题目的线段树需要实现以下两个功能。
1、查询 [0 , R)的 bi - ci + 1的最小值
2、更新[0, R)范围内所有的 bi - ci + 1
那么我们需要让线段树上保存两个值
1、[0, R)区间内集体加上的值(dat)
2、[0 , R)内去掉第1点剩余部分的最小值(rmq)。
这样对于查询操作时,只需要加上所有父节点及自己的 dat,和自身的rmq即可。
对于更新操作时,只需要更新自己的dat,
并递归所有父节点的 rmq[parent]=min(rmq[lch]+dat[lch],rmq[rch]+dat[rch])即可。
总结:每次利用RMQ求出 a1 <= num范围内的 bi - ci + 1的最小值,与num进行比较,即可知道这个数字是需要选择,接下来在树上废弃掉 bi <= num的节点即可不会被已经经过的区间影响,同时如果这个数字被选择,更新 a1 <= num的全部节点 为原来的值+1,这样代表这些区间内都加入了一个有效元素,以为下次循环做铺垫。
三、代码
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> P;
P sortByA[50007], sortByB[50007];
int rmq[131072], dat[131072], a[50009], b[50009], c[50009], n, _current, maxt, ans;
void input()
{scanf("%d", &n);maxt = 0, ans = 0;for (int i = 0; i < n; i++){scanf("%d%d%d", &a[i], &b[i], &c[i]);maxt = max(maxt, b[i]);sortByA[i].first = a[i];sortByA[i].second = i;}sort(sortByA, sortByA + n);for (int i = 0; i < n; i++){sortByB[i].first = b[sortByA[i].second];sortByB[i].second = i;}sort(sortByB, sortByB + n);
}
void build(int i, int l, int r)
{if (r - l == 1){rmq[i] = b[sortByA[l].second] - c[sortByA[l].second] + 1;}else{int lch = i * 2 + 1;int rch = i * 2 + 2;build(lch, l, (l + r) / 2);build(rch, (l + r) / 2, r);rmq[i] = min(rmq[lch], rmq[rch]);}
}
int query(int _l, int _r, int i, int l, int r)
{if (_l >= r || _r <= l){return 50007;}else if (l >= _l && r <= _r){return rmq[i] + dat[i];}else{if (dat[i] > 50000){return dat[i];}int lch = query(_l, _r, i * 2 + 1, l, (l + r) / 2);int rch = query(_l, _r, i * 2 + 2, (l + r) / 2, r);return min(lch, rch) + dat[i];}
}
void update(int _l, int _r, int i, int l, int r, int v)
{if (_l >= r || _r <= l){}else if (l >= _l && r <= _r){dat[i] += v;int j = i;while (j > 0){j = (j - 1) / 2;rmq[j] = min(rmq[j * 2 + 1] + dat[j * 2 + 1], rmq[j * 2 + 2] + dat[j * 2 + 2]);}}else{update(_l, _r, i * 2 + 1, l, (l + r) / 2, v);update(_l, _r, i * 2 + 2, (l + r) / 2, r, v);}
}
void solveItem()
{int idx = upper_bound(sortByA, sortByA + n, P(_current, 0x3f3f3f3f)) - sortByA;if (idx <= 0){return;}int mint = query(0, idx, 0, 0, n);if (mint == _current){update(0, idx, 0, 0, n, 1);ans++;}int past = lower_bound(sortByB, sortByB + n, P(_current, -0x3f3f3f3f)) - sortByB;for (int j = past; j < n; j++){if (sortByB[j].first != _current){break;}update(sortByB[j].second, sortByB[j].second + 1, 0, 0, n, 50007);}
}
void solve()
{for (int i = 0; i <= maxt; i++){_current = i;solveItem();}
}
int main()
{input();build(0, 0, n);solve();printf("%d\n", ans);return 0;
}