排位赛一 D meetings

题目

有两个牛棚位于一维数轴上的点 0 和 L 处。同时有 N 头奶牛位于数轴上不同的位置(将牛棚和奶牛看作点)。每头奶牛 i 初始时位于某个位置 xi,并朝着正向或负向以一个单位每秒的速度移动,用一个等于 1 或 −1 的整数 di​ 表示。每头奶牛还拥有一个在范围 [1,10^3]内的重量。所有奶牛始终以恒定的速度移动,直到以下事件之一发生:如果奶牛 i 移动到了一个牛棚,则奶牛 iii 停止移动。当奶牛 i 和 j 占据了相同的点的时候,并且这一点不是一个牛棚,则发生了相遇。此时,奶牛 i 被赋予奶牛 j 先前的速度,反之亦然。注意奶牛可能在一个非整数点相遇。令 T 等于停止移动的奶牛(由于到达两个牛棚之一)的重量之和至少等于所有奶牛的重量之和的一半的最早时刻。请求出在时刻 0…T(包括时刻 T)之间发生的奶牛对相遇的总数。输入格式输入的第一行包含两个空格分隔的整数 N 和 L。以下 N 行,每行包含三个空格分隔的整数 wi,xi​ 以及 di。所有的位置 xi各不相同,并且满足 0<xi<i​<L。

解法

蚂蚁
和蚂蚁挺像的,相撞掉头等同于交换了质量继续前进
既然如此,最后到达牛棚的就是朝着离他们牛棚前进的最近的那些牛(只是可能交换了重量),把奶牛先按位置排序,二分一下最小的时间,从左往右扫过去,相遇的对数 = 往左走的奶牛所碰到往右走的奶牛的数量之和。
(代码也有挺多注释的)

代码

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 5e4 + 100;
int n, L, sum_weight, f[maxn], k;

struct COW {
	int weight, pos, dir;
} cow[maxn];

bool cmp(COW a, COW b) {
	return a.pos < b.pos;
}


bool judge(int x) {
	int l = 1, r = n, weight = 0;
	for(int i=1; i<=n; ++i) { //枚举每只母牛
		if(cow[i].dir == 1) {
			if(cow[i].pos + x >= L) weight += cow[r--].weight;

		}
		else {
			if(cow[i].pos - x<=0) weight += cow[l++].weight;
		}
	}
	return weight*2 >= sum_weight;
}


int main() {
	scanf("%d%d", &n, &L); //母牛数量 数轴终点

	for(int i=1; i<=n; ++i) {	//母牛的体重, 位置, 初始方向
		scanf("%d%d%d", &cow[i].weight, &cow[i].pos, &cow[i].dir);
		sum_weight += cow[i].weight;
	}

	//按位置排序, 因为最后到达两端的一定是靠近端点的那些母牛,只是可能变化了体重
	sort(cow+1, cow+1+n, cmp);


	int l = 1, r = 1e9;
	while(l + 1 < r) { //二分时间, 最终r就是所需时间
		int mid = (l+r) / 2;
		if(judge(mid)) r = mid;
		else l = mid;
	}

	int ans = 0;

	for(int i=1; i<=n; ++i) {
		if(cow[i].dir == -1) {
			int pos = cow[i].pos - r * 2; //向左走r,迎面走来的母牛走r,
										  //相对位置就是 向右的母牛不动,而他走了2r
			int l1 = 0, r1 = k+1;
			while(l1 + 1 < r1) { //二分向右的母牛的位置
				int mid=  (l1 + r1) / 2;
				if(f[mid] >= pos) r1 = mid;
				else l1 = mid;
			}
			ans += k - r1 + 1;
		} else f[++k] = cow[i].pos;
	}


	cout << ans << endl;

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值