我是靠谱客的博主 风中音响,这篇文章主要介绍hdu 5862(离线化+线段树+扫描线),现在分享给大家,希望可以做个参考。

题意:
给定 n 条水平或竖直的线段,统计所有线段的交点个数。 n<=100000

解题思路:

是一种叫做扫描线的算法,扫描线就是解决线段之间交点个数的问题,这道题还是相对比较简单的,因题目保证每条线段都是竖直或水平的,我用的是线段树做的,当然也可以用树状数组来做效率会更高。将横线加入线段树,碰到竖线的时候进行查询操作,查询的值就是交点的个数。

一条竖线用一个数据类型来存,横线的两个端点则用两个数据结构来存,左端点代表在线段树中插入,右端点表示在线段树中删除。当插入、删除、查询的x值一样时要满足先加点,后查询最后删点,这一点用type处理一下,可以减少判断。详细看代码。
先学习一下扫描线算法在看这道题还是挺简单的。

复制代码
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <iostream> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <ctime> using namespace std; typedef long long ll; typedef pair<int, int> pii; #define pb push_back #define mp make_pair #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define calm (l+r)>>1 const int INF = 2139062143; const int maxn=200010; int n,m;///n是线段树的大小 struct line { int type; ///type的处理非常巧妙,0代表查插入点,1代表查询点,2代表删除点 ///对type排序之后就满足先插入再查询最后删点 int x,y1,y2; }p[maxn<<1]; struct Seg{ int tag[maxn<<2];///tag记录节点更新的值 int sum[maxn<<2]; inline void pushup(int rt){ sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } inline void pushdown(int rt,int l,int r){ if(tag[rt]!=0){ int m=calm; ll llen=m-l+1; ll rlen=r-m; tag[rt<<1]+=tag[rt];tag[rt<<1|1]+=tag[rt]; sum[rt<<1]+=tag[rt]*llen; sum[rt<<1|1]+=tag[rt]*rlen; tag[rt]=0; } } // void print(int l,int r,int rt){///输出l——r的最底部节点 // if(l==r){ // printf("%I64d ",sum[rt]); // return; // } // pushdown(rt,l,r); // int m=calm; // print(lson);print(rson); // } void build(int l,int r,int rt){///建树 tag[rt]=0; if(l==r){ //scanf("%I64d",&sum[rt]);///在建树是进行输入 //sum[l]=0; sum[rt]=0; return; } int m=calm; build(lson);build(rson); pushup(rt); } void add(int L,int R,int v,int l,int r,int rt){///将区间L—-R内的值加v if(L<=l&&r<=R){ sum[rt]+=(ll)v*(r-l+1); tag[rt]+=v; return; } pushdown(rt,l,r); int m=calm; if(L<=m)add(L,R,v,lson); if(R>m)add(L,R,v,rson); pushup(rt); } ll query(int L,int R,int l,int r,int rt){///返回L——R区间内的和 if(L<=l&&r<=R){ return sum[rt]; } pushdown(rt,l,r); int m=calm; ll ans=0; if(L<=m)ans+=query(L,R,lson); if(R>m)ans+=query(L,R,rson); return ans; } }tree; int yy[maxn<<1];///离散化用 bool cmp(line a,line b) { if(a.x==b.x) return a.type<b.type; return a.x<b.x; } int main() { // freopen("1006.in","r",stdin); // freopen("out.out","w",stdout); int T; scanf("%d",&T); while(T--) { int cntx=0,cnty=0; scanf("%d",&n); int x1,x2,y1,y2; int tot=0,cnt=0;//tot总的结构体数,cnt的个数 for(int i=1;i<=n;i++) { scanf("%d%d%d%d",&x1,&y1,&x2,&y2); yy[++cnt]=y1; yy[++cnt]=y2; if(x1==x2)///竖线查询 { p[++tot].type=1; p[tot].x=x1; p[tot].y1=min(y1,y2); p[tot].y2=max(y1,y2); } else///横线 { p[++tot].type=0;///0代表横线的起点 p[tot].x=min(x1,x2); p[tot].y1=y1; p[tot].y2=1; p[++tot].type=2;///2代表横线的终点 p[tot].x=max(x1,x2); p[tot].y1=y1; p[tot].y2=-1; } } sort(yy+1,yy+cnt+1); sort(p+1,p+tot+1,cmp);///接下来是进行离散化 cnty=unique(yy+1,yy+2*n+1)-yy-1; for(int i=1;i<=tot;i++) { if(p[i].type==1) { p[i].y1=lower_bound(yy+1,yy+cnty+1,p[i].y1)-yy; p[i].y2=lower_bound(yy+1,yy+cnty+1,p[i].y2)-yy; } else { p[i].y1=lower_bound(yy+1,yy+cnty+1,p[i].y1)-yy; } } tree.build(1,cnty,1); ll ans=0; for(int i=1;i<=tot;i++) { if(p[i].type==1) { ans+=tree.query(p[i].y1,p[i].y2,1,cnty,1); } else { tree.add(p[i].y1,p[i].y1,p[i].y2,1,cnty,1); } } printf("%I64dn",ans); } return 0; }

最后

以上就是风中音响最近收集整理的关于hdu 5862(离线化+线段树+扫描线)的全部内容,更多相关hdu内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(60)

评论列表共有 0 条评论

立即
投稿
返回
顶部