题目分析:
把题目要求的转化一下,首先二分答案 ,那么就是判断是否存在一组 满足
因为原题目要求不升,那么令,就去掉了约束条件,与此同时以及的意义就改为自己那组的的前缀和。
,就是看是否有满足,如果有就满足条件。
,对每个,把看做横座标,看做纵座标,相当于可以在这条射线上动,那么二者求和后取就是横座标和纵座标的较小值。
于是问题等价于在条射线上选一个点(向量),求和后使其落在第一象限。
如果初始时没有射线在第一象限,那么等价于判断是否所有射线(点)都在一个过原点的半平面内。
可以设一个过原点的方向向量代表一个半平面,其中。一个点要在此半平面左侧,则。对于个点都要满足限制,如果无解则说明二分的值可以达到。
,设这个半超平面的法向量为,其中. 一个点在此半超平面左侧,则与法向量的点积 。即。
(也可以用这个方法,其实更通用的方法是设为一个半超平面,带入点就是)
此时的限制就是组,这相当于是一个二维平面上的半平面交(看做横座标,看做纵座标),如果交出来无解则说明二分的值可以达到。(注意不要漏掉)
限制写成向量的形式可以分类讨论一下,先令分别等于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);
}