poj 3067 Japan 【树状数组】

<题目链接>

题目大意:

有两个点集,这两个点集从上至下分别从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\n",++ncase,ans);
49     }
50     return 0;
51 }

 

 

2018-10-17

posted @ 2018-10-17 14:55  悠悠呦~  阅读(178)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end