BZOJ3190 [JLOI2013]賽車(單調棧+半平面交)

【題解】

數形結合思考:
畫出v-t圖像,若一條直線在一、四象限有不被覆蓋的部分,它代表的車就可以領跑 
將直線按他們的斜率從小到大排序後,要維護一個下凸殼,由於一條新加入的直線,能覆蓋的左邊的直線是從右向左單調的,所以用單調棧來維護 

需要注意的細節:
1. 要在y軸右邊做半平面交,我加入了一條過原點,斜率負無窮大的直線,覆蓋掉了所有直線的左邊一半 
2. 平行線無法算交點,重疊的直線都要算,要特殊判斷 


【代碼】

#include<stdio.h>
#include<stdlib.h>
#define eps 1e-13
double b[10005],k[10005];
int num[10005],ans[10005],sta[10005];
void jh(int* a,int* b)
{
	int t=*a;
	*a=*b;
	*b=t;
}
void jh(double* a,double* b)
{
	double t=*a;
	*a=*b;
	*b=t;
}
void kp(int low,int high)
{
	int i=low,j=high;
	double mk=k[(i+j)/2],mb=b[(i+j)/2];
	while(i<j)
	{
		while( k[i]<mk || (k[i]==mk&&b[i]<mb) ) i++;
		while( k[j]>mk || (k[j]==mk&&b[j]>mb) ) j--;
		if(i<=j)
		{
			jh(&k[i],&k[j]);
			jh(&b[i],&b[j]);
			jh(&num[i],&num[j]);
			i++;
			j--;
		}
	}
	if(j>low) kp(low,j);
	if(i<high) kp(i,high);
}
void kpans(int low,int high)
{
	int i=low,j=high,mid=ans[(i+j)/2];
	while(i<j)
	{
		while(ans[i]<mid) i++;
		while(ans[j]>mid) j--;
		if(i<=j)
		{
			jh(&ans[i],&ans[j]);
			i++;
			j--;
		}
	}
	if(j>low) kpans(low,j);
	if(i<high) kpans(i,high);
}
double getx(double k1,double b1,double k2,double b2)
{
	return (b2-b1)/(k1-k2);
}
int main()
{
	int n,i,j,top=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
		scanf("%lf",&b[i]);
	for(i=1;i<=n;i++)
		scanf("%lf",&k[i]);
	for(i=1;i<=n;i++)
		num[i]=i;
	n++;//加入直線y=0
	k[n]=-1/eps;
	kp(1,n);
	for(i=1;i<=n;i++)
	{
		if(i>1&&k[i]==k[i-1])
		{
			if(b[i]==b[i-1])
			{
				sta[++top]=i;
				continue;
			}
			else
			{
				j=i-1;
				while(j>=1&&sta[top]==j) top--;
			}
		}
		while( top>1 && getx(k[i],b[i],k[sta[top]],b[sta[top]])-getx(k[sta[top-1]],b[sta[top-1]],k[sta[top]],b[sta[top]])<=-eps ) top--;
		sta[++top]=i;
	}
	for(i=1;i<=top;i++)
		ans[i]=num[sta[i]];
	kpans(1,top);
	printf("%d\n",top-1);
	for(i=2;i<=top;i++)
	{
		if(ans[i]!=0) printf("%d",ans[i]);
		if(i<top) printf(" ");
	}
	return 0;
}


發佈了110 篇原創文章 · 獲贊 8 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章