暑假-树状数组-I - Japan

题意:在西边有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;
}


巧妙地将一些二元关系转换成二维坐标点,简化问题。

知道点的总是+上方点的数目,即可求出下方点的数目。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值