点击打开链接poj 3067
思路:线段树
分析:
1 题目要求的是找到所有直线的交点总数,并且题目明确指出两条直线之间最多只有一个交点
2 很明显我们应该先对这些直线进行排序:按照左边的编号从小到大,左边编号相同时按照右边编号从小到大。那么假设现在有一条直线1-3,那么能够和这条直线有交点的肯定是右边的编号大于3的,那么这个过程就可以利用线段树的查找,查找完毕还要更新线段树。
3 由于题目的n最大5*10^5,那么最坏的情况1+2+...n,会超过int , 所以我们应该选择long long.
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define MAXN 1000010 int n , m , k; struct Point{ int x; int y; }; Point p[MAXN]; struct Node{ int left; int right; int sum; }; Node node[4*MAXN]; bool cmp(Point a , Point b){ if(a.x < b.x) return true; else if(a.x == b.x && a.y < b.y) return true; return false; } void buildTree(int left , int right , int pos){ node[pos].left = left; node[pos].right = right; node[pos].sum = 0; if(left == right) return; int mid = (left+right)>>1; buildTree(left , mid , pos<<1); buildTree(mid+1 , right , (pos<<1)+1); } int query(int left , int right , int pos){ if(node[pos].left == left && node[pos].right == right) return node[pos].sum; int mid = (node[pos].left + node[pos].right)>>1; if(right <= mid) return query(left , right , pos<<1); else if(left > mid) return query(left , right , (pos<<1)+1); else return query(left , mid , pos<<1)+query(mid+1 , right , (pos<<1)+1); } void update(int index , int pos){ if(node[pos].left == node[pos].right){ node[pos].sum++; return; } int mid = (node[pos].left+node[pos].right)>>1; if(index <= mid) update(index , pos<<1); else update(index , (pos<<1)+1); node[pos].sum = node[pos<<1].sum + node[(pos<<1)+1].sum; } int main(){ int t , Case = 1; long long ans;//选择long long scanf("%d" , &t); while(t--){ scanf("%d%d%d" , &n , &m , &k); for(int i = 0 ; i < k ; i++) scanf("%d%d" , &p[i].x , &p[i].y); sort(p , p+k , cmp); buildTree(1 , m , 1); ans = 0; for(int i = 0 ; i < k ; i++){ if(p[i].y < m) ans += query(p[i].y+1 , m , 1); update(p[i].y , 1); } printf("Test case %d: %lld\n" , Case++ , ans); } return 0; }
思路:树状数组
分析:
1 题目要求的是找到所有直线的交点总数,并且题目明确指出两条直线之间最多只有一个交点At most two superhighways cross at one location
2 我们应该先对这些直线进行排序:按照左边的编号从小到大,左边编号相同时按照右边编号从小到大。那么假设现在有一条直线1-3,那么能够和这条直线有交点的肯定是右边的编号大于3的,那么这个过程就可以利树状数组求和得到,求和完毕还要更新树状数组。
3 由于题目的k没有给定,那么最坏的情况1+2+...k
4 数据会超过int , 所以我们应该选择long long
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N = 1010; const int MAXN = 1000010; struct Node{ int x; int y; bool operator<(const Node& s)const{ if(x < s.x) return true; else if(x == s.x && y < s.y) return true; return false; } }; Node node[MAXN]; int n , m , k; long long treeNum[N]; int lowbit(int x){ return x&(-x); } long long getSum(int x){ long long sum = 0; while(x){ sum += treeNum[x]; x -= lowbit(x); } return sum; } void add(int x , int val){ while(x < N){ treeNum[x] += val; x += lowbit(x); } } long long solve(){ long long ans = 0; memset(treeNum , 0 , sizeof(treeNum)); sort(node , node+k); for(int i = 0 ; i < k ; i++){ ans += i-getSum(node[i].y); add(node[i].y , 1); } return ans; } int main(){ int cas = 1; int Case; scanf("%d" , &Case); while(Case--){ scanf("%d%d%d" , &n , &m , &k); for(int i = 0 ; i < k ; i++) scanf("%d%d" , &node[i].x , &node[i].y); printf("Test case %d: %lld\n" , cas++ , solve()); } return 0; }