[CF924D]Contact ATC

280 篇文章 1 订阅

题目

传送门 to CF

传送门 to VJ

思路

毫无疑问,到达塔台的时间是 − x v \frac{-x}{v} vx (可以从向量角度思考)。

所以,如果进行风速为 v v v 的微调之后,二者能够同时到达,即 − x i v i + v = − x j v j + v \frac{-x_i}{v_i+v}=\frac{-x_j}{v_j+v} vi+vxi=vj+vxj

可以解出(交叉相乘就好) ( x i − x j ) v = v i x j − x i v j (x_i-x_j)v=v_ix_j-x_iv_j (xixj)v=vixjxivj

稍微处理一下,把每一项都除以 x i x j x_i x_j xixj ,得到 ( 1 x j − 1 x i ) v = v i x i − v j x j \left(\frac{1}{x_j}-\frac{1}{x_i}\right)v=\frac{v_i}{x_i}-\frac{v_j}{x_j} (xj1xi1)v=xivixjvj

很明显,这个式子的意义是 v v v ( − 1 x i , v i x i ) , ( − 1 x j , v j x j ) (-\frac{1}{x_i},\frac{v_i}{x_i}),(-\frac{1}{x_j},\frac{v_j}{x_j}) (xi1,xivi),(xj1,xjvj) 两个点的斜率。

这两个点的形式是一样的,就会让题目变的简单起来:有 n n n 个点,求有多少点对,满足斜率的绝对值不超过 w w w

把这些点按照 x x x 坐标排个序,然后用所有的点减去不可行的点。更准确地说,

在这里插入图片描述

存储一个红色的线(斜率 − w -w w ),一个橙色的线(斜率 + w +w +w ),这种不可行的点就刚好满足其中一个条件:

  • 红色的线高于当前点的红色线。
  • 橙色的线低于当前点的橙色线。

这可以用平衡树进行维护。或者,你直接将其离散化,树状数组求取。

p . s . p.s. p.s. 之所以满足 ∣ w ∣ < min ⁡ i = 1 n ∣ v i ∣ |w|<\min_{i=1}^{n}|v_i| w<mini=1nvi ,是因为这样一来,无需验证是否有因为风而无法到达的飞机。

代码

卡精度,需要手写分数类。

#include <cstdio>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
inline int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0' or c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c and c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
inline void writeint(long long x){
	if(x < 0) putchar('-'), x = -x;
	if(x > 9) writeint(x/10);
	putchar((x%10)^48);
}
# define MB template < class T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }

struct Fraction{
	typedef long long int_;
	int_ son, mom; // 分“子”,分“母”
	Fraction(const int_ &x=0,const int_ &y=1){
		int_ d = __gcd(x,y);
		son = x/d, mom = y/d; // 值为x/y
		if(mom < 0) son = -son, mom = -mom;
	}
	bool operator < (const Fraction &that) const {
		return son*that.mom < mom*that.son;
	}
	Fraction operator * (const int &num) const {
		return Fraction(son*num,mom);
	}
	Fraction operator + (const Fraction &that) const {
		return Fraction(son*that.mom+mom*that.son,mom*that.mom);
	}
	Fraction operator - () const {
		return Fraction(-son,mom);
	}
	bool operator == (const Fraction &that) const {
		return son == that.son and mom == that.mom;
	}
};
struct Point{
	Fraction x, y;
	bool operator < (const Point &that) const {
		return x < that.x;
	}
};
const int MaxN = 100005;
Point p[MaxN]; int n, w;
Fraction l[MaxN], r[MaxN];
int c1[MaxN], c2[MaxN];

int main(){
	n = readint(), w = readint();
	for(int i=1,x,v; i<=n; ++i){
		x = readint(), v = readint();
		p[i].x = Fraction(1,x);
		p[i].y = Fraction(-v,x);
	}
	for(int i=1; i<=n; ++i)
		l[i] = (-p[i].x)*w+p[i].y;
	for(int i=1; i<=n; ++i)
		r[i] = (-p[i].x)*(-w)+p[i].y;
	sort(l+1,l+n+1); int L = unique(l+1,l+n+1)-l;
	sort(r+1,r+n+1); int R = unique(r+1,r+n+1)-r;
	sort(p+1,p+n+1); long long ans = 0;
	for(int i=1; i<=n; ++i){
		int tmp = 0; // 合法的点

		Fraction now = (-p[i].x)*w+p[i].y;
		int id = lower_bound(l+1,l+L,now)-l;
		for(int j=id-1; j; j-=(j&-j)) tmp -= c1[j];
		for(int j=id; j<=n; j+=(j&-j)) ++ c1[j];

		now = (-p[i].x)*(-w)+p[i].y;
		id = lower_bound(r+1,r+R,now)-r;
		for(int j=id; j; j-=(j&-j)) tmp += c2[j];
		for(int j=id; j<=n; j+=(j&-j)) ++ c2[j];

		ans += tmp;
	}
	printf("%lld\n",ans);
	return 0;
}

后记

另一个很好的想法,来自于@C202044zxy 的博客

如果风速最大时,我比它先到;风速最小时,我比它晚到,那么中间存在一个时刻,我俩同时到达。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值