POJ 3067 原来是树状数组--真的涨姿势

题意:计划在东边的城市和西边的城市中建路,东边的点从1.....n,西边的点从1......m,求这些点连起来后有多少个交叉。

PS:这个题目没有任何思路,没想到是树状数组。。。。

交叉出5个点

分析:3,1肯定能和1与2,3,4连线,2与2,3,4的连线相交。即x,y连线肯定和a(小于x),b(大于y)的连线,或者a(大于x),b(小于y)的连线相交。就看有几条这种连线。因此可以先排序,然后直接看当前x,y的前边比y大的数目有几个.就是逆序对数,可参考POJ2299和POJ2352

 1 ///逆序对数是求前边有几个比当前更大的数字
 2 ///POJ2352  是求前边有几个比当前更小的数字
 3 ///按照从大到小排序后求前边有几个次序比他更小的就是逆序对数
 4 #include<cstdio>
 5 #include<iostream>
 6 #include<cstring>
 7 #include<algorithm>
 8 #define ll long long
 9 #define repu(i, a, b) for(int i = a; i < b; i ++)
10 using namespace std;
11 const int MAXN = 500010;
12 ll c[MAXN];
13 int n,m;
14 struct S
15 {
16     int x,y;
17     bool operator < (const S& s) const
18     {
19         if(x == s.x)
20             return y < s.y;
21         else
22             return x < s.x;
23     }
24 } a[MAXN];
25 int b[MAXN];
26 int lowbit(int x)
27 {
28     return x&(-x);
29 }
30 ll getsum(int i)
31 {
32     ll s = 0;
33     while(i>0)
34     {
35         s += c[i];
36         i -= lowbit(i);
37     }
38     return s;
39 }
40 void add(int li)
41 {
42     while(li<=m)///并不明白这里的结束条件是什么
43     {
44         c[li] += 1ll;
45         li += lowbit(li);
46     }
47 }
48 int main()
49 {
50     int T,kase = 0;
51     scanf("%d",&T);
52     while(T--)
53     {
54         int k;
55         scanf("%d%d%d",&n,&m,&k);
56         int x,y;
57         memset(c,0,sizeof(c));
58         for(int i=1; i<=k; i++)
59             scanf("%d%d",&a[i].x,&a[i].y);
60         ll sum = 0;
61         sort(a+1,a+k+1);
62         for(int i=1; i<=k; i++)
63         {
64             add(a[i].y);
65             sum += i-getsum(a[i].y);
66         }
67         kase++;
68         printf("Test case %d: %lld
",kase,sum);
69     }
70     return 0;
71 }
转化之后也是逆序对数
原文地址:https://www.cnblogs.com/ACMERY/p/4764689.html