20200603 hz T3 安排【分數規劃,差分→線性約束→半平面交】

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

題目分析:

把題目要求的轉化一下,首先二分答案 Z=minx>1dxd1Z=\frac {\min_{x>1} d_x}{d_1},那麼就是判斷是否存在一組 xix_i 滿足(mini=1m1j=1n(Ai,jZBj)xj)0xi0xi>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
因爲原題目要求cc不升,那麼令xj=cjcj+1x_j=c_j-c_{j+1},就去掉了約束條件,與此同時Ai,jA_{i,j}以及BjB_j的意義就改爲自己那組的aa的前綴和。

m=2m=2,就是看是否有jj滿足Ai,jzBj0A_{i,j}-z*B_j\ge0,如果有就滿足條件。

m=3m=3,對每個jj,把A1,jZBjA_{1,j}-ZB_j看做橫座標,A2,jZBjA_{2,j}-ZB_j看做縱座標,xjx_j相當於可以在這條射線上動,那麼二者求和後取minmin就是橫座標和縱座標的較小值。
於是問題等價於在nn條射線上選一個點(向量),求和後使其落在第一象限。
如果初始時沒有射線在第一象限,那麼等價於判斷是否所有射線(點)都在一個過原點的半平面內。
可以設一個過原點的方向向量(1,k)(-1,k)代表一個半平面,其中k>0k>0。一個點(x,y)(x,y)要在此半平面左側,則(1,k)×(x,y)0(-1,k)\times(x,y)\ge 0。對於nn個點都要滿足限制,如果kk無解則說明二分的值可以達到。

m=4m=4,設這個半超平面的法向量爲(1,a,b)(1,a,b),其中a>0,b>0a>0,b>0. 一個點在此半超平面左側,則與法向量的點積 0\le 0。即(1,a,b)(x,y,z)0(1,a,b)\cdot (x,y,z)\le 0
m=2m=2也可以用這個方法,其實更通用的方法是設ax+by+zax+by+z爲一個半超平面,帶入點就是au+bv+w0au+bv+w\le 0

此時的限制就是nnx+ay+bz0x+ay+bz\le 0,這相當於是一個二維平面上的半平面交(aa看做橫座標,bb看做縱座標),如果交出來無解則說明二分的值可以達到。(注意不要漏掉a>0,b>0a>0,b>0
限制寫成向量的形式可以分類討論一下,先令a,ba,b分別等於0求出線上兩點,如果y>0y>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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章