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 t0s0w的概率。

解题思路

思路1(应该不是正解)

首先这肯定是一道数学题
(首先定义 l e n ( [ a , b ] ) = b − a len([a, b]) = b-a len([a,b])=ba
在数学上一种很有用的方法叫做消元,因此先考虑 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([t0w,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 t0R,有无限个,我们不能通过常规方式求和,这里自然想到积分。于是:
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])t1t2P(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=t2t1t0=t1t21P(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 t0s0=w s 0 − t 0 = w s_0-t_0 = w s0t0=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  阅读(43)  评论(0)    收藏  举报