2016 UESTC Training for Data Structures E - 卿学姐与城堡的墙 CDOJ 1341 树状数组 逆序对 离散化

E题:卿学姐与城堡的墙

N条直线,以y=kx+b的形式给出,问有多少种方式任取两个直线,使这两个直线的交点在x=ux=v之间

对于两个直线ab,怎么判断他们的交点在x=ux=v之间呢

假如直线ax=u时的纵坐标为yau,在x=v时的纵坐标为yav,直线bx=u时的纵坐标为ybu,在x=v时的纵坐标为ybv。假设yauybu的下面,但是yav却在ybv的上面,那中间就是一定有交点的,反之如果yauybu上面,但是yav却在ybv下面,也是一定有交点的,当然还有yau==ybuyav==ybv两种情况。

首先不考虑相等的情况,就是可以把直线按yu升序排序,然后把yv离散化,然后用树状数组维护yv,然后对于每一个yv,大概思路就是查询它的树状数组的前缀和看看前面有多少个比它小的,用总数减一下就可以知道有多少个比它大的,就肯定存在那么多交点(就是相当于逆序对)。

然后我们再继续考虑细节,首先可以按yu升序排序,然后对于每条直线,如果它和上条直线的yu相同,那我们记录这个yu的值的最早出现的位置,假设为last,当前位置为i,则ans +=i-last,因为它可以和前面所有相同的都构成交点,

那么就是考虑右边是否有相同的,右边的话,我们肯定是找大于等于当前yv的数有几个,肯定就是有几个交点,然后加入当前是第i条直线,那么之前有i条直线,然后先树状数组统计小于yv的有几条,假设为x条,那么ans+=i-x,然后树状数组更新当前的yv就行

 

数据水了,貌似没有在有多条直线交在x=u的同一个点和或者交在x=v上的同一个点的情况,各种姿势都能花式水过去


代码:

#include <cstdio>
#include <cstdlib>
#include <algorithm>
using namespace std;
#define ll long long
#define maxn 200005
struct Line
{
	ll y1, y2;
	bool operator < (Line x)const
	{
		return y1 < x.y1;
	}
}line[maxn];
int N;
ll u, v, data[maxn];
ll tree[maxn];
void update(int i, ll val)
{
	while (i <= N)
	{
		tree[i] += val;
		i += i&(-i);
	}
}
ll query(int i)
{
	ll sum = 0;
	while (i)
	{
		sum += tree[i];
		i -= i&(-i);
	}
	return sum;
}
int main()
{
	//freopen("input.txt", "r", stdin);
	scanf("%d%lld%lld", &N, &u, &v);
	ll a, b;
	for (int i = 0; i < N; ++i)
	{
		scanf("%lld%lld", &a, &b);
		line[i].y1 = a*u + b, line[i].y2 = a*v + b;
		data[i] = line[i].y2;
	}
	sort(line, line + N);
	sort(data, data + N);
	int num = unique(data, data + N) - data;
	for (int i = 0; i < N; ++i)
		line[i].y2 = lower_bound(data, data + num, line[i].y2) - data + 1;
	ll ans = 0;
	int last = 0;
	for (int i = 0; i < N; ++i)
	{
		if (i > 0 && line[i].y1 == line[i - 1].y1)
		{
			//++ans;
			ans += i - last;
		}
		else
		{
			ans += i - query(line[i].y2 - 1);
			last = i;
		}
		update(line[i].y2, 1);
	}
	printf("%lld\n", ans);
	//system("pause");
	//while (1);
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值