UVA-11722 Joining with Friend

题目链接

题目大意

给定一个整数 w w w和两个闭区间 [ t 1 , t 2 ] [t_1, t_2] [t1​,t2​], [ s 1 , s 2 ] [s_1,s_2] [s1​,s2​], t 0 , s 0 t_0, s_0 t0​,s0​分别是这两个闭区间里的两个随机数,求 ∣ t 0 − s 0 ∣ ≤ w |t_0-s_0| \leq w ∣t0​−s0​∣≤w的概率。

解题思路

思路1(应该不是正解)

首先这肯定是一道数学题
(首先定义 l e n ( [ a , b ] ) = b − a len([a, b]) = b-a len([a,b])=b−a)
在数学上一种很有用的方法叫做消元,因此先考虑 t 0 t_0 t0​一定的情况,显然的是,如果 t 0 t_0 t0​一定,答案应该是 P ( t = t 0 ) = l e n ( [ t 0 − w , t 0 + w ] ∩ [ s 1 , s 2 ] ) l e n ( [ s 1 , s 2 ] ) P(t = t_0) = \frac{len([t_0-w,t_0+w] \cap [s_1, s_2])}{len([s_1,s_2])} P(t=t0​)=len([s1​,s2​])len([t0​−w,t0​+w]∩[s1​,s2​])​,这当然是极易求值的。但是呢,这道题 t 0 t_0 t0​在 [ t 1 , t 2 ] [t_1,t_2] [t1​,t2​]中变化,因此我们要求的答案应该是 P ˉ ( t = t 0 ) \bar{P}(t = t_0) Pˉ(t=t0​)其中 t 0 ∈ [ t 1 , t 2 ] t_0 \in [t_1,t_2] t0​∈[t1​,t2​](即在闭区间 [ t 1 , t 2 ] [t_1,t_2] [t1​,t2​]上 P P P的平均值)。但是由于 t 0 ∈ R t_0 \in R t0​∈R,有无限个,我们不能通过常规方式求和,这里自然想到积分。于是:
a n s = ∫ t 1 t 2 P ( t = t 0 ) d t 0 l e n ( [ t 1 , t 2 ] ) ans = \frac{\displaystyle\int^{t_2}_{t_1}P(t=t_0)dt_0}{\displaystyle len([t_1,t_2])} ans=len([t1​,t2​])∫t1​t2​​P(t=t0​)dt0​​
怎么求积分呢,通过观察 P ( t = t 0 ) P(t=t_0) P(t=t0​)可知,这个函数的图像是分段的,但是每一段都是直线,而且分段点都只能是整数,因此,对于计算机而言,穷举闭区间 [ t 1 , t 2 ] [t_1,t_2] [t1​,t2​]上所有相邻整数的中间点再求和便是结果,即:
a n s = ∑ t 0 = t 1 t 2 − 1 P ( t = t 0 + ( t 0 + 1 ) 2 ) t 2 − t 1 ans = \frac{\displaystyle \sum^{t_2-1}_{t_0=t1}P(t=\frac{t_0+(t_0+1)}{2})}{\displaystyle t_2 - t_1} ans=t2​−t1​t0​=t1∑t2​−1​P(t=2t0​+(t0​+1)​)​
那么代码如下:

#include<cmath>
#include<cstdio>
using namespace std;
#define ffopen(name) freopen(name".in", "r", stdin); freopen(name".out", "w", stdout)

int t1, t2, s1, s2, w;

inline double P(double t0){
	double g1, g2;
	g1 = t0 - w;
	g2 = t0 + w;
	if(g2 < s1) return 0.0;
	if(g1 > s2) return 0.0;
	return (fmin(s2, g2) - fmax(s1, g1)) / (s2 - s1);
}

int main(){
	ffopen("testdata");
	int T;
	scanf("%d", &T);
	for(int tn = 1; tn <= T; tn++){
		scanf("%d%d%d%d%d", &t1, &t2, &s1, &s2, &w);
		double ans = 0;
		for(int t0 = t1; t0 < t2; t0++){
			ans += P(t0 + 0.5);
		}
		ans /= (t2 - t1);
		printf("Case #%d: %.8f\n", tn, ans);
	}
	return 0;
}

思路2(这个应该是正解)

我们既然已经想到了定义区间的长度,而这个题有两个区间,自然的,长度乘长度就是面积了,因此这个题的正解应该是线性规划(毕竟积分太难了)。
那么这里,任意一对满足 t 0 ∈ [ t 1 , t 2 ] , s 0 ∈ [ s 1 , s 2 ] t_0 \in [t_1,t_2],s_0 \in [s_1,s_2] t0​∈[t1​,t2​],s0​∈[s1​,s2​]的实数对 ( t 0 , s 0 ) (t_0,s_0) (t0​,s0​)便是可能发生的一种情况。
所以, l e n ( [ t 1 , t 2 ] ) × l e n ( [ s 1 , s 2 ] ) len([t_1,t_2]) \times len([s_1,s_2]) len([t1​,t2​])×len([s1​,s2​])就应该为总可能性,这是一个矩形的面积,而满足要求的解应该是位于 t 0 − s 0 = w t_0-s_0=w t0​−s0​=w和 s 0 − t 0 = w s_0-t_0 = w s0​−t0​=w两条直线之间,裸的线性规划问题,求直线与矩形的交点再分情况讨论就好。
代码如下:

#include<cmath>
#include<cstdio>
using namespace std;
#define ffopen(name) freopen(name".in", "r", stdin); freopen(name".ans", "w", stdout)

int t1, t2, s1, s2, w;
long double S, S1, S2;
int sw1, sw2, tw1, tw2;

int main(){
	ffopen("testdata");
	int T;
	scanf("%d", &T);
	for(int tn = 1; tn <= T; tn++){
		scanf("%d%d%d%d%d", &t1, &t2, &s1, &s2, &w);
		
		S = (t2 - t1) * (s2 - s1);
		
		sw1 = t1 + w; sw2 = t2 + w;
		tw1 = s1 - w; tw2 = s2 - w;
		if(sw1 > s2){
			S1 = 0.0L;
		}else if(sw1 < s1){
			if(sw2 > s2) S1 = 0.5L * (tw1 - t1 + tw2 - t1) * (s2 - s1);
			else if(sw2 < s1) S1 = S;
			else S1 = S - 0.5L * (t2 - tw1) * (sw2 - s1);
		}else{
			if(sw2 > s2) S1 = 0.5L * (s2 - sw1) * (tw2 - t1);
			else S1 = 0.5L * (s2 - sw1 + s2 - sw2) * (t2 - t1);
		}
		
		sw1 = t1 - w; sw2 = t2 - w;
		tw1 = s1 + w; tw2 = s2 + w;
		if(sw1 > s2){
			S2 = 0.0L;
		}else if(sw1 < s1){
			if(sw2 > s2) S2 = 0.5L * (tw1 - t1 + tw2 - t1) * (s2 - s1);
			else if(sw2 < s1) S2 = S;
			else S2 = S - 0.5L * (t2 - tw1) * (sw2 - s1);
		}else{
			if(sw2 > s2) S2 = 0.5L * (s2 - sw1) * (tw2 - t1);
			else S2 = 0.5L * (s2 - sw1 + s2 - sw2) * (t2 - t1);
		}
		
		printf("Case #%d: %.8Lf\n", tn, (S2 - S1) / S);
	}
	return 0;
}
posted @ 2022-04-10 11:10  TobyShi  阅读(64)  评论(0)    收藏  举报