题意

有两个给定的凸包,小凸包严格在大凸包内部,现在要在大凸包的边界上随机放置一个光源,问小凸包的边被光源照到的期望长度为?

思路

这题是比较直观的。由于要算的是期望长度,因此可以单独计算小凸包中每一条边对最终期望的贡献。

image-20220325150039711

对于小凸包上的每一条边$M$,如果能被光源找到,则光源一定被放置在$M$的延长线与大凸包相交的线段的某一侧。

那么,只要我们能够快速求出可能放置光源位置的长度,就可以求出对应的概率,而该概率再乘上$M$的长度就是这一条边对最后期望的贡献。

可以利用凸包本身的单调性求解:维护一个双指针$L,R$,确定当前的延长线与大凸包的哪些边相交。设小凸包当前边为$\vec{u}$,大凸包的两个交点分别为$P_L,P_R$,那么有两种情况:

  • $\vec{u}$与$\vec{P_LP_R}$同向,则得知当前要求的是从$L$到$R$这一段的距离
  • $\vec{u}$与$\vec{P_LP_R}$异向,则得知当前要求的是从$R$到$L$这一段的距离。

而后,可以预处理前缀和快速求解这一段完整的边的长度,再依据这两个分类讨论不完整的线段的长度加在一起。

由于可能出现$L>R$的情况,前缀和讨论比较复杂,因此可以事先将存着长度数组复制一遍化环为链,当出现$L>R$的情况时,只要求$[L,R+siz]$这段的区间和就可以了。

另外,在维护双指针的时候,直线与线段相交有三种情况,如果维护不好其中有些情况可能会重合。其中一个比较重要的点是,我们可以规定若直线与线段是非规范相交的,则交点必然是该线段的起点,这样可以使每一情况都是独立的。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#pragma GCC optimize("-fdelete-null-pointer-checks,inline-functions-called-once,-funsafe-loop-optimizations,-fexpensive-optimizations,-foptimize-sibling-calls,-ftree-switch-conversion,-finline-small-functions,inline-small-functions,-frerun-cse-after-loop,-fhoist-adjacent-loads,-findirect-inlining,-freorder-functions,no-stack-protector,-fpartial-inlining,-fsched-interblock,-fcse-follow-jumps,-fcse-skip-blocks,-falign-functions,-fstrict-overflow,-fstrict-aliasing,-fschedule-insns2,-ftree-tail-merge,inline-functions,-fschedule-insns,-freorder-blocks,-fwhole-program,-funroll-loops,-fthread-jumps,-fcrossjumping,-fcaller-saves,-fdevirtualize,-falign-labels,-falign-loops,-falign-jumps,unroll-loops,-fsched-spec,-ffast-math,Ofast,inline,-fgcse,-fgcse-lm,-fipa-sra,-ftree-pre,-ftree-vrp,-fpeephole2",3)
#pragma GCC target("avx", "sse2")
#define IO ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr)

#include <bits/stdc++.h>
#define maxn 800005
using namespace std;

const long double eps = 1e-8;
int sgn(long double x){
if(fabs(x) < eps) return 0;
return x > eps ? 1 : -1;
}
template<typename T> struct TP{
T x, y;
friend TP operator +(const TP &a, const TP &b){
return {a.x + b.x, a.y + b.y};
}
friend TP operator -(const TP &a, const TP &b){
return {a.x - b.x, a.y - b.y};
}
friend T operator *(const TP &a, const TP &b){
return a.x * b.x + a.y * b.y;
}
friend T operator ^(const TP &a, const TP &b){
return a.x * b.y - a.y * b.x;
}
friend bool operator ==(const TP &a, const TP &b){
return fabs(a.x - b.x) < eps && fabs(a.y - b.y) < eps;
}
int toleft(const TP &b){
auto p = (*this) ^ b;
return (p > eps) - (p < -eps);
}
T len(){
return sqrt(x * x + y * y);
}
T distance(TP &b){
return ((*this) - b).len();
}
void print(){
cout << "[Point] " << x << " " << y << '\n';
}
}; using Point = TP<long double>;

template<typename T> struct TL{
TP<T> s, e;
int relation(TP<T> &p){
int c = sgn((p - s) ^ (e - s));
if(c < 0) return 1;
if(c > 0) return 2;
return 3;
}
int linecrossseg(TL &v){
int d1 = sgn((e - s) ^ (v.s - s));
int d2 = sgn((e - s) ^ (v.e - s));
if((d1 ^ d2) == -2) return 2;
return (d1 == 0 || d2 == 0);
}
TP<T> crosspoint(TL &v){
auto a1 = (v.e - v.s) ^ (s - v.s);
auto a2 = (v.e - v.s) ^ (e - v.s);
return {(s.x * a2 - e.x * a1) / (a2 - a1), (s.y * a2 - e.y * a1) / (a2 - a1)};
}
}; using Line = TL<long double>;

struct ConvexHull{
int siz;
long double alen;
vector<Point> p; vector<Line> l;
vector<long double> len;
void build(vector<Point> &o){
auto nxt = [&](int i){ return i == p.size() - 1 ? 0 : i + 1; };
auto pre = [&](int i){ return i == 0 ? p.size() - 1 : i - 1; };
siz = o.size();
for(int i = 0; i < siz; i++) p.push_back(o[i]);
for(int i = 0; i < siz; i++){
l.push_back({o[i], o[nxt(i)]});
len.push_back(o[i].distance(o[nxt(i)]));
alen += len.back();
}
}
}B, S;

int n, m;
vector<Point> iB, iS;
long double querySum(int L, int R, vector<long double> &sum){
if(R < L) return 0;
if(L == 0) return sum[R];
return sum[R] - sum[L - 1];
}
long double solve(){
auto nxt = [&](int i, vector<Line> &p){ return i == p.size() - 1 ? 0 : i + 1; };
auto pre = [&](int i, vector<Line> &p){ return i == 0 ? p.size() - 1 : i - 1; };
long double exp = 0;
vector<long double> sum, val = B.len; vector<Line> out = B.l, in = S.l;
val.insert(val.end(), val.begin(), val.end());
sum.resize(val.size());
partial_sum(val.begin(), val.end(), sum.begin());
int siz = in.size(), sizo = out.size(), L = 0, R = 0;
for(int i = 0; i < siz; i++){
long double result = 0;
while(!in[i].linecrossseg(out[L]) ||
(in[i].linecrossseg(out[L]) == 1 && in[i].crosspoint(out[L]) == out[L].e)) L = nxt(L, out);
while(!in[i].linecrossseg(out[R]) ||
(in[i].linecrossseg(out[R]) == 1 && in[i].crosspoint(out[R]) == out[R].e) || L == R) R = nxt(R, out);
Point crossL = in[i].crosspoint(out[L]);
Point crossR = in[i].crosspoint(out[R]);
if((crossR - crossL) * (in[i].e - in[i].s) > 0){
int FL = L, FR = R;
if(FL > FR) FR += sizo;
long double nowLen = querySum(FL + 1, FR - 1, sum) +
crossL.distance(out[L].e) +
crossR.distance(out[R].s);
result += nowLen * S.len[i];
} else{
int FL = R, FR = L;
if(FL > FR) FR += sizo;
long double nowLen = querySum(FL + 1, FR - 1, sum) +
crossR.distance(out[R].e) +
crossL.distance(out[L].s);
result += nowLen * S.len[i];
}
exp += result;
}
return exp / B.alen;
}

signed main(void)
{
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i++){
long double x, y;
scanf("%Lf %Lf", &x, &y);
iB.push_back({x, y});
}
for(int i = 1; i <= m; i++){
long double x, y;
scanf("%Lf %Lf", &x, &y);
iS.push_back({x, y});
}
B.build(iB); S.build(iS);
printf("%.15Lf\n", solve());
return 0;
}

HACK数据

这里放一些数据,方便读者自测。

Case#1: Input

1
2
3
4
5
6
7
8
9
4 4
0 0
5 0
5 5
0 5
3 5
0 3
4 0
5 2

Case#1: Output

1
3.888185834356963

Case#2: Input

1
2
3
4
5
6
7
8
4 3
0 0
5 0
5 5
0 5
1 1
4 1
4 4

Case#2: Output

1
4.221320343559643

Case#3: Input

image-20220325152028269

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
7 7
0 0
6 1
7 5
6 8
3 8
1 6
0 3
2 4
3 3
5 3
6 4
5 6
4 7
3 7

Case#3: Output

1
4.396949086650624

Case#4: Input

本组数据为GYM的Test#3。

image-20220325151707452

1
2
3
4
5
6
7
8
9
10
11
12
13
8 4
-1000000000 -1
-1 -1000000000
1 -1000000000
1000000000 -1
1000000000 1
1 1000000000
-1 1000000000
-1000000000 1
-1 -1
1 -1
1 1
-1 1

Case#4: Output

1
3.999999997171573