poj 3067 Japan

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




你可能感兴趣的:(poj 3067 Japan)