hiho_1079_离散化
题目
在长度为L的宣传栏上张贴N张海报,将宣传栏分为L个长度为1的单位,海报长度为整数,且高度和宣传栏相同,左右边界和宣传栏单位之间缝隙重合(即海报总是跨越整数个单位)。后贴的海报可能会覆盖之前贴的海报的全部或者部分,问N张海报贴完之后,没有被完全覆盖的海报的总数。N <= 10^6, L <= 10^9.
分析
区间覆盖问题,会想到用线段树来解决,直观的将L个单位视为线段树的叶子节点,但是这样做空间复杂度太高(同样时间复杂度也很高),可以将区间进行离散化:
将所有海报的边界先收集起来,按照从小到大排序后去重,这样得到的离散点映射到从0开始,每次增加1的一个区间上,然后将该区间映射到线段树上。
线段树的节点结构中维护两个信息:(1)该节点代表的区间是否只有一个海报single_post,(2)如果只有一个海报,则记录海报序号post。
插入海报的时候,找到海报的左右边界点对应到线段树的叶节点编号构成的区间A,然后从根节点开始递归,如果到达某个节点,该节点代表区间B和A相同,则设置该节点的single_post为true,且post为海报号,否则向下找;
最后查询的时候,从线段树根节点开始向下找,找到所有的区间内只有一个海报的节点,并将海报号记录下来。
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | #include<iostream> #include<string.h> #include<iostream> #include<queue> #include<cmath> #include<unordered_map> #include<unordered_set> #include<string> #include<vector> #include<algorithm> using namespace std; const int inf = 1 << 29; const int kMax = 200005; unordered_map< int , int > discrete_map; //海报所贴的端点到线段树区间端点的映射 int gPost[kMax >> 1][2]; //海报的左右边界 vector< int > gPoints; //存放海报的端点 unordered_set< int > uncovered_post; //没有被遮盖的海报序号 struct Node{ int beg; int end; int post; bool single_post; Node(){ beg = end = 0; single_post = true ; post = -1; } }; Node gNodes[kMax << 2]; //连续型的区间 线段树 void BuildTree( int node, int beg, int end){ gNodes[node].beg = beg; gNodes[node].end = end; if (beg + 1 == end){ //连续型的区间,线段树的叶子节点表示一个单位长度[i, i+1],而不是一个点[i, i]. return ; } int mid = (beg + end) >> 1; int left = 2 * node + 1, right = 2 * node + 2; BuildTree(left, beg, mid); BuildTree(right, mid, end); //连续型的区间,[beg, mid] & [mid, end]; 而离散型的区间[beg, mid]&[mid + 1, end] } void PushDown( int node){ if (gNodes[node].beg + 1 == gNodes[node].end) return ; if (gNodes[node].single_post){ int left = 2 * node + 1, right = 2 * node + 2; gNodes[left].post = gNodes[right].post = gNodes[node].post; gNodes[left].single_post = gNodes[right].single_post = true ; gNodes[node].single_post = false ; } } void Post( int node, int beg, int end, int post){ if (gNodes[node].beg == beg && gNodes[node].end == end){ gNodes[node].single_post = true ; gNodes[node].post = post; return ; } PushDown(node); int left = 2 * node + 1, right = 2 * node + 2; int mid = (gNodes[node].beg + gNodes[node].end) / 2; if (beg >= mid){ Post(right, beg, end, post); } else if (end <= mid){ Post(left, beg, end, post); } else { Post(left, beg, mid, post); Post(right, mid, end, post); } } void Query( int node){ if (gNodes[node].single_post && gNodes[node].post != -1){ uncovered_post.insert(gNodes[node].post); return ; } if (gNodes[node].beg + 1 == gNodes[node].end){ return ; } int left = 2 * node + 1, right = 2 * node + 2; Query(left); Query(right); } int main(){ int N, L; scanf( "%d %d" , &N, &L); for ( int i = 0; i < N; i++){ scanf( "%d %d" , &gPost[i][0], &gPost[i][1]); //将各个离散点加入vector gPoints.push_back(gPost[i][0]); gPoints.push_back(gPost[i][1]); } //先排序 sort(gPoints.begin(), gPoints.end()); //对vector去重 gPoints.resize(std::distance(gPoints.begin(), unique(gPoints.begin(), gPoints.end()))); BuildTree(0, 0, gPoints.size() - 1); //将离散化的点,按照大小映射到一个连续的区间,缩小数据规模 for ( int i = 0; i < gPoints.size(); i++){ discrete_map[gPoints[i]] = i; } for ( int i = 0; i < N; i++){ Post(0, discrete_map[gPost[i][0]], discrete_map[gPost[i][1]], i); } Query(0); int result = uncovered_post.size(); printf( "%d\n" , result); return 0; } |
标签:
onlinejudge
, 区间数据结构和算法
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· ThreeJs-16智慧城市项目(重磅以及未来发展ai)
· .NET 原生驾驭 AI 新基建实战系列(一):向量数据库的应用与畅想
· Ai满嘴顺口溜,想考研?浪费我几个小时
· Browser-use 详细介绍&使用文档
· 软件产品开发中常见的10个问题及处理方法