zoj 3157 Weapon 线段树求逆序对数

题目链接:http://www.icpc.moe/onlinejudge/showProblem.do?problemId=3128

题意:平面上有n条直线,给出l, r,求出这些直线的交点横坐标在(l, r)范围内的个数。

思路:

首先求出每条直线与直线x = l和直线x = r的交点,如下图。

因为题目要求区间(l, r)是开区间,所以为了避免交点的横坐标刚好是l或者r的情况,可以先把l加上一个很小的值,r减去一个很小的值,如图中的灰线。

然后求出各条直线与两条灰线的交点,首先按与l的交点的y值从小到大排序,然后标上一个id。

然后再按与r的交点的y值从小到大排序。

如果两条直线line1和line2,如果line1.ly < line2.ly,则如果line1.ry > line2.ry,那么这个交点肯定落在范围内。

所以此时排序后的id序列是3 1 2,很明显就是求这个序列的逆序对数。然后用线段树求出逆序对数就可以了。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 int n;
 4 #define maxn 10010
 5 #define eps 1e-8
 6 #define lson l, m, rt<<1
 7 #define rson m+1, r, rt<<1|1
 8 struct Line
 9 {
10     double k, b;
11     Line(double kk, double bb){k = kk; b = bb;}
12     Line(){}
13 }l[maxn];
14 double L, R;
15 struct Node
16 {
17     double l, r;
18     int id;
19     Node(double ll, double rr, int ii){l = ll; r = rr; id = ii;}
20     Node(){}
21 }node[maxn];
22 bool cmp1(Node a, Node b){return a.l < b.l;}
23 bool cmp2(Node a, Node b){return a.r < b.r;}
24 int sum[maxn<<2];
25 void pushup(int rt)
26 {
27     sum[rt] = sum[rt<<1] + sum[rt<<1|1];
28 }
29 void build(int l, int r, int rt)
30 {
31     if(l == r)
32     {
33         sum[rt] = 0; return;
34     }
35     int m = (l+r)>>1;
36     build(lson);
37     build(rson);
38     pushup(rt);
39 }
40 void update(int pos, int add, int l, int r, int rt)
41 {
42     if(l == r)
43     {
44         sum[rt] += add; return;
45     }
46     int m = (l+r)>>1;
47     if(pos <= m) update(pos, add, lson);
48     else update(pos, add, rson);
49     pushup(rt);
50 }
51 int query(int L, int R, int l, int r, int rt)
52 {
53     if(L <= l && r <= R)
54     {
55         return sum[rt];
56     }
57     int m = (l+r)>>1;
58     int ret = 0;
59     if(L <= m) ret += query(L, R, lson);
60     if(R > m) ret += query(L, R, rson);
61     return ret;
62 }
63 int main()
64 {
65    // freopen("in.txt", "r", stdin);
66     while(~scanf("%d", &n))
67     {
68         for(int i = 1; i <= n; i++)
69         {
70             double x1, y1, x2, y2;
71             scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
72             double k = (y2-y1)/(x2-x1);
73             double b = y1 - k*x1;
74             l[i].k = k; l[i].b = b;
75         }
76         scanf("%lf%lf", &L, &R);
77         L += 1e-8; R -= 1e-8;
78         for(int i = 1; i <= n; i++)
79         {
80             node[i].l = l[i].k*L + l[i].b;
81             node[i].r = l[i].k*R + l[i].b;
82         }
83         sort(node+1, node+1+n, cmp1);
84         for(int i = 1; i <= n; i++) node[i].id = i;
85
86         sort(node+1, node+1+n, cmp2);
87         int ans = 0;
88         build(1, n, 1);
89         for(int i = 1; i <= n; i++)
90         {
91             update(node[i].id, 1, 1, n, 1);
92             ans += i - query(1, node[i].id, 1, n, 1);
93         }
94         printf("%d\n", ans);
95
96     }
97     return 0;
98 }
时间: 2024-11-06 17:44:33

zoj 3157 Weapon 线段树求逆序对数的相关文章

线段树求逆序数方法 HDU1394&amp;&amp;POJ2299

为什么线段树可以求逆序数? 给一个简单的序列 9 5 3 他的逆序数是3 首先要求一个逆序数有两种方式:可以从头开始往后找比当前元素小的值,也可以从后往前找比当前元素大的值,有几个逆序数就是几. 线段树就是应用从后往前找较大值得个数.(一边更新一边查) 当前个数是 n = 10 元素   9  5   3 9先加入线段树,T[9]+=1:查从T[9]到T[10]比9大的值,没有sum = 0: 5 加入线段树,T[5] += 1,查从T[5]到T[10]比5大的值,有一个9,sum +=1: 3

权值线段树求逆序对问题

我们都知道,求逆序对数量可以用归并排序解决.但是用归并排序只能解决静态的序列问题,没有扩展的区间.因此就有了用权值线段树求逆序对的方法. 1 #include<iostream> 2 #include<iomanip> 3 #include<ctime> 4 #include<climits> 5 #include<algorithm> 6 #include<queue> 7 #include<vector> 8 #inc

hdu1394--Minimum Inversion Number(线段树求逆序数,纯为练习)

Minimum Inversion Number Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 10326 Accepted Submission(s): 6359 Problem Description The inversion number of a given number sequence a1, a2, ..., an is t

HDU 1394 Minimum Inversion Number(线段树求逆序数)

题目地址:HDU 1394 这题可以用线段树来求逆序数. 这题的维护信息为每个数是否已经出现.每次输入后,都从该点的值到n-1进行查询,每次发现出现了一个数,由于是从该数的后面开始找的,这个数肯定是比该数大的.那就是一对逆序数,然后逆序数+1.最后求完所有的逆序数之后,剩下的就可以递推出来了.因为假如目前的第一个数是x,那当把他放到最后面的时候,少的逆序数是本来后面比他小的数的个数.多的逆序数就是放到后面后前面比他大的数的个数.因为所有数都是从0到n-1.所以比他小的数就是x,比他大的数就是n-

BNU 2418 Ultra-QuickSort (线段树求逆序对)

题目链接:http://acm.bnu.edu.cn/bnuoj/problem_show.php?pid=2418 解题报告:就是给你n个数,然后让你求这个数列的逆序对是多少?题目中n的范围是n < 500000,所以,暴力是不行的.还是第一次学会用线段树求逆序数,这种方法的时间复杂度是n * log n,是不是很快呢,利用了线段树查询速度快的优势.具体的方法如下: 这里先说一下,如果输入的n个数不是连续的,也就是说把这n个数按从小到大的顺序排列起来不是连续的话,还要先离散化一下,其实也就是把

HDU 1394 Minimum Inversion Number(线段树求逆序对)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1394 解题报告:给出一个序列,求出这个序列的逆序数,然后依次将第一个数移动到最后一位,求在这个过程中,逆序数最小的序列的逆序数是多少? 这题有一个好处是输入的序列保证是0 到 n-1,所以不许要离散化,还有一个好处就是在计算在这个序列中比每个数大和小的数一共有多少个的时候可以在O(1)时间计算出来,一开始我没有意识到,还傻傻的用了两层for循环来每次都计算,当然这样果断TLE了.把一个数从第一个移

hdoj 1394 Minimum Inversion Number【线段树求逆序对】

求逆序对有很多算法,这里说一下线段树求逆序对的思想. 知识点:线段树,逆序对,单点更新,成段求和 算法:线段树求逆序数的前提条件是要离散化,变成连续的点,首先建树,每个节点设置一个num值为0. 然后根据逆序对的定义,前面出现过的比当前数大的个数的和,我们需要求前面的比他大的数,其实就相当于从当前a[i]点对他后面所有出现过的数求和一次.然后把当前点的值在线段树叶子节点变为1,表示出现过,并向上更新到线段树里面.比如说样例4 2 1 5 3 首先4后面没有值,4更新为1,4--5区间更新为1,1

HDU 4911 http://acm.hdu.edu.cn/showproblem.php?pid=4911(线段树求逆序对)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4911 解题报告: 给出一个长度为n的序列,然后给出一个k,要你求最多做k次相邻的数字交换后,逆序数最少是多少? 因为每次相邻的交换操作最多只能减少一个逆序对,所以最多可以减少k个逆序对,所以我们只要求出原来的序列有多少个逆序对然后减去k再跟0取较大的就可以了. 因为数据范围是10的五次方,所以暴力求肯定会TLE,所以要用n*logn算法求逆序对,n*logn算法有几种可以求逆序对的: 线段树,树状数

HDU - 1394 Minimum Inversion Number (线段树求逆序数)

Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seq