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\n",kase,sum); 69 } 70 return 0; 71 }
人生就像心电图,想要一帆风顺,除非game-over