题目大意
左右两个竖排,左边竖排有N个点,从上到下依次标记为1,2,...N; 右边竖排有M个点,从上到下依次标记为1,2....M。现在从K条直线分别连接左边一个点和右边一个点,求这K条直线的交点个数(左右竖排上的点不算做交点)。
给出N,M,K,以及K条线的起点和终点。
题目分析
求两两交点的问题最好固定顺序,如i和i之前的交点,这样便于统计而不重复不遗漏。在将K条线按照左边点从小到大的顺序进行排序,左边点相同按照右边点从小到大排序之后,按照顺序分析当前线和它之前的线的交点个数:
当前线k的左边点序号为 xa, 右边点序号为 ya, 则对于当前线k之前的那些线1--k-1,他们左边点的序号肯定小于等于xa, 这些线(1--k-1)中右边点序号大于 ya的那些线会和当前线k有一个交点。因此对于当前线k,统计之前1---k-1线的右边点在 [ya + 1--M]中的个数,形成了一个区间统计问题。
考虑右边点1,2....M 各对应一个统计变量 count[i], 每次分析线k,都将线k的右边点ya 对应的count[ya] ++。 这样,每次都统计 [t, M]区间内 count[i]的和。 使用树状数组来实现。
实现(c++)
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<vector> #include<algorithm> #include<string.h> using namespace std; #define MAX_CITY_NUM 1002 struct Highway{ int east_city; int west_city; Highway(int e, int w): east_city(e), west_city(w){ } }; vector<Highway> gHws; int gC[MAX_CITY_NUM]; int gLowbit[MAX_CITY_NUM]; bool Cmp(const Highway& h1, const Highway& h2){ if (h1.east_city == h2.east_city) return h1.west_city < h2.west_city; return h1.east_city < h2.east_city; } void InitLowbit(int n){ for (int i = 1; i <= n; i++){ gLowbit[i] = i&(-i); } } void InitSequence(int n){ memset(gC, 0, sizeof(gC)); } void Update(int k, int n, int add){ while (k <= n){ gC[k] += add; k += gLowbit[k]; } } int Query(int k){ int result = 0; while (k > 0){ result += gC[k]; k -= gLowbit[k]; } return result; } int main(){ int cas, N, M, K, e_city, w_city; scanf("%d", &cas); InitLowbit(1001); for (int c = 1; c <= cas; c++){ scanf("%d %d %d", &N, &M, &K); gHws.clear(); InitSequence(M); for (int i = 1; i <= K; i++){ scanf("%d %d", &e_city, &w_city); gHws.push_back(Highway(e_city, w_city)); } sort(gHws.begin(), gHws.end(), Cmp); long long int crossing = 0; for (int i = 0; i < K; i++){ crossing += (Query(M) - Query(gHws[i].west_city)); Update(gHws[i].west_city, M, 1); } printf("Test case %d: %lld ", c, crossing); } return 0; }