题目分析:
把题目要求的转化一下,首先二分答案
Z
=
min
x
>
1
d
x
d
1
Z=\frac {\min_{x>1} d_x}{d_1}
Z=d1minx>1dx,那么就是判断是否存在一组
x
i
x_i
xi 满足
(
min
i
=
1
m
−
1
∑
j
=
1
n
(
A
i
,
j
−
Z
∗
B
j
)
∗
x
j
)
≥
0
,
∀
x
i
≥
0
,
∃
x
i
>
0
\left(\min_{i=1}^{m-1} \sum_{j=1}^n(A_{i,j}-Z*B_j)*x_j\right)\ge 0,\forall x_i\ge 0,\exist x_i>0
(i=1minm−1j=1∑n(Ai,j−Z∗Bj)∗xj)≥0,∀xi≥0,∃xi>0
因为原题目要求
c
c
c不升,那么令
x
j
=
c
j
−
c
j
+
1
x_j=c_j-c_{j+1}
xj=cj−cj+1,就去掉了约束条件,与此同时
A
i
,
j
A_{i,j}
Ai,j以及
B
j
B_j
Bj的意义就改为自己那组的
a
a
a的前缀和。
m = 2 m=2 m=2,就是看是否有 j j j满足 A i , j − z ∗ B j ≥ 0 A_{i,j}-z*B_j\ge0 Ai,j−z∗Bj≥0,如果有就满足条件。
m
=
3
m=3
m=3,对每个
j
j
j,把
A
1
,
j
−
Z
B
j
A_{1,j}-ZB_j
A1,j−ZBj看做横坐标,
A
2
,
j
−
Z
B
j
A_{2,j}-ZB_j
A2,j−ZBj看做纵坐标,
x
j
x_j
xj相当于可以在这条射线上动,那么二者求和后取
m
i
n
min
min就是横坐标和纵坐标的较小值。
于是问题等价于在
n
n
n条射线上选一个点(向量),求和后使其落在第一象限。
如果初始时没有射线在第一象限,那么等价于判断是否所有射线(点)都在一个过原点的半平面内。
可以设一个过原点的方向向量
(
−
1
,
k
)
(-1,k)
(−1,k)代表一个半平面,其中
k
>
0
k>0
k>0。一个点
(
x
,
y
)
(x,y)
(x,y)要在此半平面左侧,则
(
−
1
,
k
)
×
(
x
,
y
)
≥
0
(-1,k)\times(x,y)\ge 0
(−1,k)×(x,y)≥0。对于
n
n
n个点都要满足限制,如果
k
k
k无解则说明二分的值可以达到。
m
=
4
m=4
m=4,设这个半超平面的法向量为
(
1
,
a
,
b
)
(1,a,b)
(1,a,b),其中
a
>
0
,
b
>
0
a>0,b>0
a>0,b>0. 一个点在此半超平面左侧,则与法向量的点积
≤
0
\le 0
≤0。即
(
1
,
a
,
b
)
⋅
(
x
,
y
,
z
)
≤
0
(1,a,b)\cdot (x,y,z)\le 0
(1,a,b)⋅(x,y,z)≤0。
(
m
=
2
m=2
m=2也可以用这个方法,其实更通用的方法是设
a
x
+
b
y
+
z
ax+by+z
ax+by+z为一个半超平面,带入点就是
a
u
+
b
v
+
w
≤
0
au+bv+w\le 0
au+bv+w≤0)
此时的限制就是
n
n
n组
x
+
a
y
+
b
z
≤
0
x+ay+bz\le 0
x+ay+bz≤0,这相当于是一个二维平面上的半平面交(
a
a
a看做横坐标,
b
b
b看做纵坐标),如果交出来无解则说明二分的值可以达到。(注意不要漏掉
a
>
0
,
b
>
0
a>0,b>0
a>0,b>0)
限制写成向量的形式可以分类讨论一下,先令
a
,
b
a,b
a,b分别等于0求出线上两点,如果
y
>
0
y>0
y>0则说明应在直线左侧,否则应在直线右侧,然后根据点的符号确定方向。
普通的半平面交实际上是判不了无解的,所以我们需要在外层框一个大矩形,如果最后留下的直线小于等于两条则说明无解。
Code(eps开成1e-6会WA):
#include<bits/stdc++.h>
#define maxn 40015
using namespace std;
const double inf = 1e16, eps = 1e-8;
int n,k,a[maxn],b[maxn];
namespace task1{
void main(){
double ans=0,s[2]={0};
for(int i=1;i<=n;i++){
s[b[i]!=0]+=a[i];
ans=max(ans,s[1]/s[0]);
}
printf("%.6f\n",ans);
}
}
int A[4][maxn];
double sgn(double x){return x>eps?1:x<-eps?-1:0;}
namespace task2{
bool check(double Z){
double L=eps,R=inf;
for(int i=1;i<=n&&L<=R;i++){
double x=A[1][i]-Z*A[0][i], y=A[2][i]-Z*A[0][i];
if(!sgn(x)) {if(-y<0) return 1;}
else if(x>0) R=min(R,-y/x);
else L=max(L,-y/x);
}
return L>R;
}
}
namespace task3{
struct Pt{
double x,y;
Pt(double x=0,double y=0):x(x),y(y){}
Pt operator + (const Pt &p)const{return Pt(x+p.x,y+p.y);}
Pt operator - (const Pt &p)const{return Pt(x-p.x,y-p.y);}
double operator * (const Pt &p)const{return x*p.y-y*p.x;}
Pt operator * (const double &t)const{return Pt(x*t,y*t);}
}p[maxn];
struct Line{
Pt p,v; double ang;
Line(Pt p=0,Pt v=0):p(p),v(v),ang(atan2(v.y,v.x)){}
bool operator < (const Line &L)const{return sgn(ang-L.ang)? ang<L.ang:L.v*(p-L.p)>=0;}
}L[maxn],q[maxn];
bool Onleft(Pt p,Line L){return L.v*(p-L.p)>0;}
int cnt,l,r;
Pt Inter(Line a,Line b){return a.p+a.v*((b.v*(a.p-b.p))/(a.v*b.v));}
bool HalfPlane(){
sort(L+1,L+1+cnt);
q[l=r=1]=L[1];
for(int i=2;i<=cnt;i++) if(sgn(L[i].ang-L[i-1].ang)){
while(l<r&&!Onleft(p[r-1],L[i])) r--;
while(l<r&&!Onleft(p[l],L[i])) l++;
q[++r]=L[i],p[r-1]=Inter(q[r-1],q[r]);
}
while(l<r-1&&!Onleft(p[r-1],q[l])) r--;
return r-l+1<=2;
}
bool check(double Z){
L[cnt=1]=Line(Pt(0,0),Pt(0,-1)),L[cnt=2]=Line(Pt(0,0),Pt(1,0));
L[++cnt]=Line(Pt(inf,0),Pt(0,1)),L[++cnt]=Line(Pt(inf,inf),Pt(-1,0));
for(int i=1;i<=n;i++){
double x=A[1][i]-Z*A[0][i],y=A[2][i]-Z*A[0][i],z=A[3][i]-Z*A[0][i];
if(!sgn(y)&&!sgn(z)) {if(x>0) return 1;}
else{
if(!sgn(y)){
if(sgn(z)>0) L[++cnt]=Line(Pt(0,-x/z),Pt(-1,0));
else L[++cnt]=Line(Pt(0,-x/z),Pt(1,0));
}
else if(!sgn(z)){
if(sgn(y)>0) L[++cnt]=Line(Pt(0,-x/y),Pt(-1,0));
else L[++cnt]=Line(Pt(0,-x/y),Pt(1,0));
}
else{
if(sgn(y)==sgn(-x/z)) L[++cnt]=Line(Pt(-x/y,0),Pt(x/y,-x/z));
else L[++cnt]=Line(Pt(0,-x/z),Pt(-x/y,x/z));
}
}
}
return HalfPlane();
}
}
int main()
{
freopen("arrangement.in","r",stdin);
freopen("arrangement.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) scanf("%d",&b[i]),b[i]--;
if(k==2) {task1::main();return 0;}
for(int i=1;i<=n;i++){
for(int j=0;j<=3;j++) A[j][i]=A[j][i-1];
A[b[i]][i]+=a[i];
}
double l=0,r=1e10,mid;
while(r-l>1e-6) mid=(l+r)*0.5,(k==3?task2::check(mid):task3::check(mid))?(l=mid):(r=mid);
printf("%.6f\n",l);
}