題目分析:
把題目要求的轉化一下,首先二分答案 ,那麼就是判斷是否存在一組 滿足
因爲原題目要求不升,那麼令,就去掉了約束條件,與此同時以及的意義就改爲自己那組的的前綴和。
,就是看是否有滿足,如果有就滿足條件。
,對每個,把看做橫座標,看做縱座標,相當於可以在這條射線上動,那麼二者求和後取就是橫座標和縱座標的較小值。
於是問題等價於在條射線上選一個點(向量),求和後使其落在第一象限。
如果初始時沒有射線在第一象限,那麼等價於判斷是否所有射線(點)都在一個過原點的半平面內。
可以設一個過原點的方向向量代表一個半平面,其中。一個點要在此半平面左側,則。對於個點都要滿足限制,如果無解則說明二分的值可以達到。
,設這個半超平面的法向量爲,其中. 一個點在此半超平面左側,則與法向量的點積 。即。
(也可以用這個方法,其實更通用的方法是設爲一個半超平面,帶入點就是)
此時的限制就是組,這相當於是一個二維平面上的半平面交(看做橫座標,看做縱座標),如果交出來無解則說明二分的值可以達到。(注意不要漏掉)
限制寫成向量的形式可以分類討論一下,先令分別等於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);
}