题意:在西边有M座城市,东边有N座城市,分别都是从北到南编号依次为1,2,3...(n/m)
要建k条高速公路,每条高速公路分别从西边的一座城市连接到东边的一座城市。
问:这些高速公路有多少个交叉点。
分析:假设一条高速公路从西边的X城市链接到东边的Y城市,那么与这条高速公路相交
的高速公路有2中情况,(1)从西边编号比X小的城市连接到东边比Y大的城市。(2)从西边
编号比X大的城市链接到东边比Y小的城市。 我们可以把每一条高速公路看成二维坐标里的
一个点(x,y)。那个与它相交的高速公路的点(xi,yi)为:点(x,y)左上方和右下方的点的个数。
简单来说就是满足:(x-xi)*(y-yi)<0,那么这两个点(两条高速公路)就是相交的。
但是:由于N,M最大值为1000,如果N个每个城市都与M的每个城市有高速公路,那么总的
高速公路条数为1000*1000。直接暴力求会TLE。
TLE暴力代码:
#include<iostream>
using namespace std;
const int MAXN = 1000 + 5;
struct point
{
int x, y;
};
point city[MAXN*MAXN];
int main()
{
int t, ans = 0;
cin >> t;
while (t--)
{
long long int sum = 0;
int n, m, k;
cin >> n >> m >> k;
for (int i = 1; i <= k; i++)
{
cin >> city[i].x >> city[i].y;
}
for (int i = 1; i <= k; i++)
{
for (int j = i + 1; j <= k; j++)
{
if ((city[i].x - city[j].x)*(city[i].y - city[j].y) < 0)
{
sum++;
}
}
}
cout << "Test case" << ++ans << ": " << sum << endl;
}
return 0;
}
既然要求点(x,y)左上方和右下方的点的个数(之和再除以2,因为对于每条高速公路会被计算2次,那么可以统一只求一边)。
那么可以通过预处理(x轴升序排序)+只求点左上方的点的个数。
AC代码:
/*
排序之后对于每个点(x,y)都会出现在之前点的右边,
即固定了一个变量(x),只需要求另一个变量(y)即可,
树状数组C就是求y轴的点出现的个数。
*/
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 1000 + 5;//城市上限
struct point
{
int x, y;
//西边城市编号和东边城市编号
};
point city[MAXN*MAXN];
//高速公路上限为MAXN*MAXN
int c[MAXN];//树状数组
int lowbit(int x)//树状数组函数之一
{
return x&(-x);
}
long long int sum(int i)//树状数组函数之一
{
long long int s = 0;
while (i > 0)
{
s += c[i];
i -= lowbit(i);
}
return s;
}
bool cmp(point a, point b)//比较函数
{
if (a.x == b.x)
{
return a.y < b.y;
}
return a.x < b.x;
}
void add(int i, int val)//树状数组函数之一
{
while (i < MAXN)
{
c[i] += val;
i += lowbit(i);
}
}
int main()
{
int t, ans = 0;
scanf("%d", &t);
while (t--)
{
long long int temp = 0;
int n, m, k;
scanf("%d%d%d", &n, &m, &k);
memset(c, 0, sizeof(c));
for (int i = 0; i < k; i++)
{
scanf("%d%d", &city[i].x, &city[i].y);
}
sort(city, city + k, cmp);//按X轴升序
for (int i = 0; i < k; i++)
{
temp += sum(MAXN) - sum(city[i].y);
//求当前坐标左上方的点的个数
//可优化为:temp += i - sum(city[i].y);
//i为之前出现了多少个点,sum(city[i].y)为
//当前点左下方的点的个数,那么总的个数(即i)
//减去左下方的个数等于右上方的个数。
add(city[i].y, 1);
//出现的位置+1.
}
printf("Test case %d: %lld\n", ++ans, temp);
}
return 0;
}
知道点的总是+上方点的数目,即可求出下方点的数目。