[CF780H]Intranet of Buses

Intranet of Buses

题解

计算几何出乎意料地好调,只调了一个小时,但卡精度二分 50 50 50 次还是太离谱了。

首先,原题相当于给了我们一个奇怪 n n n 边形,让我们取该多边形的周长上 m m m 个等分点,使得它们中与相邻两个点的连线的最大长度最小。

与相邻连线的最大长度最小,这使得我们很快想到了二分这个最大长度。但它的等分点的位置不是固定的,是在不断运动的的,我们将这些等分点连接后形成的新的 m m m 边形的形状与边长都是会变的,不好直接判断。

我们先单独看一下某两个相邻的等分点点它们的连线边长变化,由于这个运动是不断循环的,我们可以只考虑 0 , C m ) 0,\frac{C}{m}) 0,mC) 的时间区间内的边长变化。

在这段时间中,这些点有可能经过不同的边,方向发生变化,也有可能一直在一条边上运动。我们可以考虑将它们的运动拆分成在不同的边对上的运动,而当它们运动的边与它们的起点终点是固定的时候,它们运动时其间之间的距离明显是一个二次函数。

我们设点 u u u v v v 的起点分别为 ( a x , a y ) , ( b x , b y ) (ax,ay),(bx,by) (ax,ay),(bx,by),运动的方向向量分别为 ( cos ⁡   α , sin ⁡   α ) , ( cos ⁡   β , sin ⁡   β ) (\cos\,\alpha,\sin\,\alpha),(\cos\,\beta,\sin\,\beta) (cosα,sinα),(cosβ,sinβ),它们运动的距离 x x x 与它们间的距离可以表示为:

y = ( a x + x cos ⁡   α − b x − x cos ⁡   β ) 2 + ( a y + x sin ⁡   α − b y − x sin ⁡   β ) 2 = ( ( cos ⁡   α − cos ⁡   β ) 2 + ( sin ⁡   α − sin ⁡   β ) 2 ) x 2 + 2 ( cos ⁡   α − cos ⁡   β + sin ⁡   α − sin ⁡   β ) x + ( a x − b x ) 2 + ( a y − b y ) 2 \begin{array}{cc} y=(ax+x\cos\,\alpha-bx-x\cos\,\beta)^2+(ay+x\sin\,\alpha-by-x\sin\,\beta)^2 \\ =((\cos\,\alpha-\cos\,\beta)^2+(\sin\,\alpha-\sin\,\beta)^2)x^2+2(\cos\,\alpha-\cos\,\beta+\sin\,\alpha-\sin\,\beta)x+(ax-bx)^2+(ay-by)^2\end{array} y=(ax+xcosαbxxcosβ)2+(ay+xsinαbyxsinβ)2=((cosαcosβ)2+(sinαsinβ)2)x2+2(cosαcosβ+sinαsinβ)x+(axbx)2+(ayby)2

而我们是想要它的长度小于我们二分的值,而它又明显是一个下凹的函数,我们可以通过求根公式将它的合法区间解出来,这对应的是多边形上一段边的区间,可以被转化为一段时间区间。

对于一个等分点,它在多边形上对应的区间是固定的,我们可以找到对于它来说合法的边的区间,转化为时间区间。由于它对应的边对有多个,所以它的合法时间区间也会有多个,我们需要将它求区间并,得到它的合法时间集合。

而我们需要使得有一个时刻,相邻的所有等分点之间距离都要小于我们二分的 m i d mid mid,相当于我们要找到对于所有合法时间集合的交集。如果存在交集,当前的距离就是合法的。

如何同时维护区间交集与并集呢? ODT!!!,不会真的有人想打吧。。。

很明显,肯定有一个合法的时间点是端点,我们相当于要找到一个被所有几何包含的端点,于是我们可以采用差分的思想。

我们可以将加入的所有合法时间区间的左右端点拿出来排序,并记录下它们的颜色(即它们属于哪一个等分点的合法时间集合)。加入左端点时对当前区间对应颜色的 c n t + + cnt++ cnt++,加入右端点时对 c n t − − cnt-- cnt。当某时刻某种颜色的 c n t cnt cnt 大于 0 0 0 时,说明它在此时合法。

我们的任务变成了排序后看有没有某一个时刻所有颜色的 c n t cnt cnt 都大于 0 0 0 ,所有等分点都合法,这就很好维护了。因为要排序,所以单次判断是 O ( n log ⁡   n ) O\left(n\log\,n\right) O(nlogn) 的。

我们只需要预处理出来每个等分点对应的分段函数,再二分满足条件的最大边长即可。

时间复杂度 O ( n log ⁡ 2   n ) O\left(n\log^2\,n\right) O(nlog2n) ,但这题精度卡得离谱,要二分 50 50 50 次。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define MAXM 400005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x7f7f7f7f7f7f7f7f;
const int mo=998244353;
const int jzm=2333;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-9;
typedef pair<double,double> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y){return x+y<mo?x+y:x+y-mo;}
int qkpow(int a,int s){int t=1;while(s){if(s&1)t=1ll*a*t%mo;a=1ll*a*a%mo;s>>=1;}return t;}
double sqr(double x){return x*x;}
int n,m,tot,cnt[MAXN],idx;double summ,alen;
struct point{
	double x,y;point(){x=y=0;}
	point(double X,double Y){x=X;y=Y;}
	point operator + (const point &rhs)const{return point(x+rhs.x,y+rhs.y);}
}a[MAXN]; 
double dist(point x,point y){return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));}
point walk(point p,double len,point agl){return p+point(agl.x*len,agl.y*len);}
struct line{
	point s,t,agl;double len,pre;line(){len=pre=0;}
	line(point S,point T){s=S;t=T;len=dist(s,t);agl=point((t.x-s.x)/len,(t.y-s.y)/len);}
}s[MAXN];
struct ming{
	double l,r,a,b,c;ming(){l=r=a=b=0;}
	ming(double L,double R,double A,double B,double C){l=L;r=R;a=A;b=B;c=C;} 
	double delta(){return sqr(b)-4.0*a*c;}
	double leftRoot(){return (-b-sqrt(delta()))/(2.0*a);}
	double rightRoot(){return (-b+sqrt(delta()))/(2.0*a);} 
}b[MAXM];
struct range{
	double wh;int col;bool typ;range(){wh=0;col=0;typ=0;}
	range(double X,int Y,bool Z){wh=X;col=Y;typ=Z;}
	bool friend operator < (const range &x,const range &y){if(Fabs(x.wh-y.wh)<eps)return x.typ>y.typ;return x.wh<y.wh;}
}d[MAXM<<2];
void insert(double L,double R){
	if(L>R)return ;
	int l=(int)floor(L/alen),r=(int)floor(R/alen);
	if(l>m)l-=m,L-=summ;if(r>m)r-=m,R-=summ;int las=idx;
	if(l==r){
		d[++idx]=range(L-alen*l,l,1);
		d[++idx]=range(R-alen*l,l,0);
	}
	else{
		d[++idx]=range(L-alen*l,l,1);d[++idx]=range(alen,l,0);
		int id=l;
		while(id!=r){
			id++;if(id>m)id-=m;if(id==r)break;
			d[++idx]=range(0.0,id,1);d[++idx]=range(alen,id,0);
		}
		d[++idx]=range(0.0,r,1);d[++idx]=range(R-alen*r,r,0);
	}
}
bool check(double mid){
	idx=0;
	for(int i=1;i<=tot;i++){
		ming tmp=b[i];tmp.c-=sqr(mid);if(tmp.delta()<0||(Fabs(tmp.a)<eps&&tmp.c>0))continue;
		insert(tmp.l+max(0.0,tmp.leftRoot()),min(tmp.r,tmp.l+tmp.rightRoot()));
	}
	sort(d+1,d+idx+1);int siz=0;
	for(int i=1;i<=m;i++)cnt[i]=0;
	for(int i=1;i<=idx;i++){
		if(d[i].typ==1)cnt[d[i].col]++;else cnt[d[i].col]--;
		if(d[i].typ==1&&cnt[d[i].col]==1)siz++;
		if(d[i].typ==0&&cnt[d[i].col]==0)siz--;
		if(siz==m)return 1;
	}
	return 0;
}
int main(){
	read(n);read(m);
	for(int i=1;i<=n;i++)scanf("%lf %lf",&a[i].x,&a[i].y);
	for(int i=1;i<n;i++)s[i]=line(a[i],a[i+1]);s[n]=line(a[n],a[1]);
	for(int i=n+1;i<=2*n;i++)s[i]=s[i-n];
	for(int i=1;i<=n;i++)summ+=s[i].len;alen=summ/(1.0*m);
	for(int i=1;i<=2*n;i++)s[i].pre=s[i-1].pre+s[i].len;
	for(int i=n+1,j=1;i<=2*n;i++){
		while(s[i-1].pre-s[j].pre>alen)j++;
		while(s[i].pre-s[j-1].pre>alen){
			point st=s[j].s,ed=s[i].s;
			if(s[i-1].pre-s[j-1].pre>alen)st=walk(st,s[i-1].pre-s[j-1].pre-alen,s[j].agl);
			if(s[i-1].pre-s[j-1].pre<alen)ed=walk(ed,alen-s[i-1].pre+s[j-1].pre,s[i].agl);
			b[++tot].a=sqr(s[i].agl.x-s[j].agl.x)+sqr(s[i].agl.y-s[j].agl.y);
			b[tot].b=2.0*(st.x-ed.x)*(s[j].agl.x-s[i].agl.x)+2.0*(st.y-ed.y)*(s[j].agl.y-s[i].agl.y);
			b[tot].c=sqr(st.x-ed.x)+sqr(st.y-ed.y);
			b[tot].l=dist(s[i].s,ed)+s[i-1].pre;
			b[tot].r=b[tot].l+min(dist(ed,s[i].t),dist(st,s[j].t));
			if(Fabs(b[tot].r-s[i].pre)<eps)break;else j++;
		}
	}
	double l=0,r=alen;int times=50;
	while(times--){double mid=(l+r)/2.0;if(check(mid))r=mid;else l=mid;}
	printf("%.7f\n",l);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值