poj 3067 Japan

来源:互联网 发布:linux系统函数api手册 编辑:程序博客网 时间:2024/05/05 05:28

点击打开链接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 1000010int 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;}



原创粉丝点击