标签:can ems 区间更新 处理 segment roo 题目 any The
Description
Input
Output
Sample Input
1 5 1 4 2 6 8 10 3 4 7 10
Sample Output
4
这里给两个例子,普通的离散化会错误。
eg1:1-10 1-4 5-10
eg2:1-10 1-4 6-10
普通离散化后都变成了[1,4][1,2][3,4]
例子1被完全覆盖了,而例子2没有被完全覆盖
但在压缩时,我们将仍然漏在外面的5给压缩掉了,所以我们在所有相隔距离大于1的点之间都插入一个单点(让它充当那段被裸露区间),然后此问题得以解决。
具体可去 https://blog.csdn.net/qq_35802619/article/details/98326267 了解
另外对c++中的unique函数 https://www.cnblogs.com/hua-dong/p/7943983.html
下面给出代码
#include <iostream> #include <algorithm> #include <string.h> #include <cstdio> #include <string> #include <cmath> #include <vector> #include <stack> #include <queue> #include <stack> #include <list> #include <map> #include <set> //#include <unordered_map> #define Fbo friend bool operator < (node a, node b) #define mem(a, b) memset(a, b, sizeof(a)) #define FOR(a, b, c) for(int a = b; a <= c; a++) #define RFOR(a,b, c) for(int a = b; a >= c; a--) #define off ios::sync_with_stdio(0) bool check1(int a) { return (a & (a - 1)) == 0 ? true : false; } using namespace std; typedef pair<int, int> pii; typedef long long ll; const int INF = 0x3f3f3f3f; const int mod = 1e9 + 7; const int Maxn = 2e5 + 5; const double pi = acos(-1.0); const double eps = 1e-8; ll a[Maxn]; ll li[Maxn], ri[Maxn]; vector<ll>num; struct node { ll l, r;//区间[l,r] ll add;//区间的延时标记 ll sum;//区间和 }tree[Maxn * 4 + 9]; ll n; void pushup(ll rt) { //向上更新 tree[rt].sum = tree[rt << 1].sum + tree[rt << 1 | 1].sum; } void pushdown(ll rt) {//向下更新 if (tree[rt].add) { //当我们的标记不是0,说明该段染色,所以往下传递 tree[rt << 1].add = tree[rt].add; tree[rt << 1 | 1].add = tree[rt].add; //这里不能用加 tree[rt].add = 0; //取消本层标记 } } void BuildTree(ll l, ll r, ll rt) { tree[rt].l = l; tree[rt].r = r; tree[rt].add = 0; if (l == r) { //tree[rt] = {l,r,0}; return; } ll mid = (l + r) >> 1; BuildTree(l, mid, rt << 1); //递归左子树 BuildTree(mid + 1, r, (rt << 1) + 1); //递归右子树 pushup(rt);//向上更新 } void updata(ll l, ll r, ll val, ll rt) { //root为结点 区间更新 //cout << l << " " << tree[rt].l << "----" << r << " " << tree[rt].r << endl; if (l <= tree[rt].l && r >= tree[rt].r) { tree[rt].add = val;//延时标记 return; } pushdown(rt); ll mid = (tree[rt].l + tree[rt].r) >> 1; if (l <= mid) { updata(l, r, val, rt << 1); } if (r > mid) { updata(l, r, val, rt << 1 | 1); } pushup(rt);//向上更新 可加可不加 本题并不需要根据儿子节点来更新父亲节点也能做 } void query(ll l, ll r, ll rt) { ////这个询问和普通线段树的询问略有不同 if (tree[rt].add) { // 如果标记存在,说明这个区间是单色的 a[tree[rt].add] = 1; //将有被染色的颜色记录下来 return; } if (tree[rt].l == tree[rt].r) //一定要这个判断条件,否则你没有递归终止条件,就RE了 return; //这与普通线段树的格式有区别,但是原理相同 pushdown(rt); ll mid = (tree[rt].l + tree[rt].r) >> 1; if (l <= mid) { query(l, r, rt << 1);//如果是0,有两种情况,一个是没有染色,一个是有多种颜色 } if (r > mid) { query(l, r, rt << 1 | 1); } } ll solve() { //离散化操作和插点操作 ll x, y; sort(num.begin(), num.end()); //先排序 num.erase(unique(num.begin(), num.end()), num.end()); ////删除重复的元素 int m = num.size(); for (int i = 0; i < m - 1; i++) { if (num[i + 1] - num[i] > 1) num.push_back((num[i + 1] - 1));//所有相隔距离大于1的点之间都插入一个单点 } //(让它充当那段被裸露区间),这样就不会漏掉点 sort(num.begin(), num.end()); BuildTree(0, num.size() - 1, 1); for (ll i = 1; i <= n; i++) { // 全开ll方便点 x = lower_bound(num.begin(), num.end(), li[i]) - num.begin(); y = lower_bound(num.begin(), num.end(), ri[i]) - num.begin(); updata(x, y, i, 1); } ll ans = 0; mem(a, 0); query(0, num.size() - 1, 1); FOR(i, 1, n) { if (a[i]) ans++; } return ans; } int main() { ll t; scanf("%lld", &t); while (t--) { scanf("%lld", &n); num.clear(); FOR(i, 1, n) { scanf("%lld%lld", &li[i], &ri[i]); num.push_back(li[i]); num.push_back(ri[i]); } printf("%lld\n", solve()); } return 0; }
标签:can ems 区间更新 处理 segment roo 题目 any The
原文地址:https://www.cnblogs.com/AlexLINS/p/12635254.html