随笔 - 97, 文章 - 22, 评论 - 81, 引用 - 0
数据加载中……

PKU 3067 Japan

题目链接:http://poj.org/problem?id=3067
/*
题意:
    左边N个点,右边M个点,分别南北方向排列,1对应1,2对应2,给出
K条记录,每条记录表示左边的A点和右边的B点相连,两条相连的先会有一
个交点,现在保证任意三个线不会交与一点,问一共多少个交点。

题解:
    树状数组

思路:
    题目求的是交点的数目,仔细观察就可以发现,如果有以下四个点,A1
,B1属于左边,A2,B2属于右边,当且仅当 A1和A2的大小关系 和 B1与B2
的大小关系 相反,于是可以联想到求一个数列的逆序数的时候用到的树状数
组的成段求和。
    我们只要将读入的整数对按左边的点递增排序,如果左边点大小相同则按
右边点递增排序,每次在树状数组中统计比当前右边的点大的数的数目,然后
再将这个点插入树状数组,这就实现了一个逆序统计。
    这里需要注意的是,统计的时候需要采用__int64,因为总的交点数可能
会超过int。最大的情况是左右两边都是1000个点,并且两两有边,那么最大
的交点数量就是:
1 * (1 + 2 +  + 999)
+
2 * (1 + 2 +  + 998)
+

998 * (1 + 2)
+
999 * 1
+
1000 * 0
    可以写个程序统计一下,发现这个数字是超过int的。
    ll ans = 0;
    for(i = 1; i <= 1000; i++) {
        ans += i * (1001 - i) * (1000 - i) / 2;
    }
*/



#include 
<iostream>
#include 
<algorithm>

using namespace std;

struct Double {
    
int l, r;
}
dt[1000100];

int N, M, K;
#define ll __int64

int cmp(Double a, Double b) {
    
if(a.l == b.l)
        
return a.r < b.r;
    
return a.l < b.l;
}


int lowbit(int x) {
    
return x & (-x);
}


ll c[
1010];
void add(int pos) {
    
while(pos <= M) {
        c[pos] 
++;
        pos 
+= lowbit(pos);
    }

}


ll sum(
int pos) {
    ll s 
= 0;
    
while(pos > 0{
        s 
+= c[pos];
        pos 
-= lowbit(pos);
    }

    
return s;
}


int main() {
    
int t;
    
int i;
    
int Case = 1;




    scanf(
"%d"&t);

    
while(t--{
        memset(c, 
0sizeof(c));
        scanf(
"%d %d %d"&N, &M, &K);
        
for(i = 0; i < K; i++{
            scanf(
"%d %d"&dt[i].l, &dt[i].r);
        }

        sort(dt, dt 
+ K, cmp);

        ll ans 
= 0;
        
for(i = 0; i < K; i++{
            ans 
+= sum(M) - sum(dt[i].r);
            add(dt[i].r);
        }

        printf(
"Test case %d: %I64d\n", Case++, ans);
    }

    
return 0;
}


posted on 2011-04-08 23:14 英雄哪里出来 阅读(1080) 评论(0)  编辑 收藏 引用 所属分类: 树状数组


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理