思路:线段树
分析:
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;
}