<题目链接>
题目大意:
有两个点集,这两个点集从上至下分别从1~n,1~m编号,现在给出n组数据,(x,y),表示左边点集编号为x的点与右边点集编号为y的点相连,现在要求计算这些线段的交点个数。
解题分析:
先将这些线段的x变量从小到大排序,若x相同,再将y从小到大排序。然后就可以直接遍历这些线段了,同时对y变量建一维树状数组,利用sum(m)-sum(node[i].y)可以很容易求出之前插入的所有线段的y值中,有多少线段的y值大于当前插入的y值,这一步相当于对排好序的线段的y值进行逆序数的求解,因为之前插入线段的x必然小于等于当前线段的x值,所以这些y值还大于node[i].y的线段必然会与当前线段产生一个交点。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 typedef long long ll; 7 const int M =1e6+10; 8 int n,m,k; 9 int tr[M]; 10 struct NODE{ 11 int x,y; 12 bool operator <(const NODE &tmp)const{ 13 if(x==tmp.x)return y<tmp.y; 14 else return x<tmp.x; 15 } 16 }node[M]; 17 inline int lowbit(int x){return x&(-x);} 18 void add(int i,int val){ 19 while(i<=m){ 20 tr[i]+=val; 21 i+=lowbit(i); 22 } 23 } 24 int sum(int i){ 25 ll ans=0; 26 while(i>0){ 27 ans+=tr[i]; 28 i-=lowbit(i); 29 } 30 return ans; 31 } 32 int main(){ 33 int T,ncase=0; 34 scanf("%d",&T); 35 while(T--){ 36 scanf("%d%d%d",&n,&m,&k); 37 for(int i=1;i<=k;i++){ 38 scanf("%d%d",&node[i].x,&node[i].y); 39 } 40 sort(node+1,node+1+k); 41 memset(tr,0,sizeof(tr)); 42 ll ans=0; 43 for(int i=1;i<=k;i++){ 44 ans+=sum(m)-sum(node[i].y); //sum(m)表示之前插入所有线段的个数,sum(node[i],y)表示之前插入的所有y值小于等于node[i].y的线段个数,因此sum(m)-sum(node[i].y)求得的是之前插入的y值中,比当前node[i].y大的线段的个数 45 //因为在该数之前插入树状数组的x值都小于等于当前线段的x值,所以这样可以求出所有线段相交的个数。 46 add(node[i].y,1); 47 } 48 printf("Test case %d: %lld ",++ncase,ans); 49 } 50 return 0; 51 }
2018-10-17