洛谷P4518 [JSOI2018]绝地反击(计算几何+二分图+退流)

题面

传送门

题解

调了咱一个上午……

首先考虑二分答案,那么每个点能够到达的范围是一个圆,这个圆与目标圆的交就是可行的区间,这个区间可以用极角来表示

首先,如果我们知道这个正\(n\)边形的转角,也就是它在水平的基础上转过了几度的话,那么可以把它的每个顶点和包含它的圆弧所代表的点连边,如果这个二分图存在完备匹配那么说明有解

然而我们并不知道这个多边形转过了几度

我们考虑一种可行的方案,如果它没有任何一个顶点和在一段圆弧的端点上,那么一定可以转一点点距离使其中一个顶点刚好落在一个圆弧的端点上,那么我们要考虑的转角实际上只有\(2n\)

发现这是个正多边形,所以我们把所有的转角对\({2\pi\over n}\)取模,那么是等价的。然后把所有的转角从小到大排序,那么转角每次增大的时候只会新增一条边或者减少一条边。那么我们就不需要重新构图了,删边的时候直接退流,加边的时候看一下有没有新的增广路就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define inf 0x3f3f3f3f
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
#define gg(u) for(int &i=cur[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
template<class T>inline bool cmax(T&a,const T&b){return a<b?a=b,1:0;}
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
const int N=605,M=N*N;const double Pi=acos(-1.0),eps=1e-8;
struct eg{int v,nx,w;}e[M];int head[N],tot;
inline void add(R int u,R int v,R int w){
	e[++tot]={v,head[u],w},head[u]=tot;
	e[++tot]={u,head[v],0},head[v]=tot;
}
int n,r,S,T,h,t,u,v,x,y,flow,cur[N],dep[N],q[N];
bool bfs(){
	fp(i,S,T)cur[i]=head[i],dep[i]=-1;
	q[h=t=1]=S,dep[S]=0;
	while(h<=t){
		u=q[h++];
		go(u)if(dep[v]<0&&e[i].w){
			dep[v]=dep[u]+1,q[++t]=v;
			if(v==T)return true;
		}
	}
	return false;
}
int dfs(int u,int lim){
	if(u==T||!lim)return lim;
	int fl=0,f;
	gg(u)if(dep[v]==dep[u]+1&&(f=dfs(v,min(lim,e[i].w)))){
		fl+=f,lim-=f,e[i].w-=f,e[i^1].w+=f;
		if(!lim)break;
	}if(!fl)dep[u]=-1;
	return fl;
}
void del(int u,int v){
	for(R int i=head[v],j=0;i;j=i,i=e[i].nx)
		if(e[i].v==u){j?(e[j].nx=e[i].nx):(head[v]=e[i].nx);break;}
	for(R int i=head[u],j=0;i;j=i,i=e[i].nx)
		if(e[i].v==v){
			j?(e[j].nx=e[i].nx):(head[u]=e[i].nx);
			if(e[i].w)return;
			break;
		}
	--flow;
	for(int i=head[S];i;i=e[i].nx)if(e[i].v==u){e[i].w^=1,e[i^1].w^=1;break;}
	for(int i=head[T];i;i=e[i].nx)if(e[i].v==v){e[i].w^=1,e[i^1].w^=1;break;}
	if(bfs())flow+=dfs(S,inf);
}
struct point{
	int x,y;double len;
	point(){}
	point(R int xx,R int yy):x(xx),y(yy){len=sqrt(x*x+y*y);}
}p[N];
struct node{
	double t;int u,v,op;
	node(){}
	node(R double tt,R int uu,R int vv,R int oop):t(tt),u(uu),v(vv),op(oop){}
	inline bool operator <(const node &b)const{return t==b.t?op>b.op:t<b.t;}
}st[N];
double alp,ql,qr,mid;
bool ck(double mid){
	fp(i,S,T)head[i]=0;tot=1;
	int top=0;
	fp(i,1,n){
		if(p[i].len+r<=mid)fp(j,1,n)add(i,j+n,1);
		else{
			R double t=acos((p[i].len*p[i].len+r*r-mid*mid)/(2*p[i].len*r));
			R double base=atan2(p[i].y,p[i].x);
			R double l=base-t,r=base+t;
			l<0?l+=2*Pi:0,r<0?r+=2*Pi:0;
			int ll=l/alp,rr=r/alp;
			st[++top]=node(l-ll*alp,i,ll+1,1),
			st[++top]=node(r-rr*alp,i,rr+1,-1);
			++ll,++rr;
			if(l<=r)fp(j,ll+1,rr)add(i,j+n,1);
			else{
				fp(j,ll+1,n)add(i,j+n,1);
				fp(j,1,rr)add(i,j+n,1);
			}
		}
	}
	fp(i,1,n)add(S,i,1),add(i+n,T,1);
	sort(st+1,st+1+top),flow=0;
	while(bfs())flow+=dfs(S,inf);
	if(flow==n)return true;
	fp(i,1,top)if(st[i].op<0)del(st[i].u,st[i].v+n);
	else{
		add(st[i].u,st[i].v+n,1);
		if(bfs())flow+=dfs(S,inf);
		if(flow==n)return true;
	}
	return false;
}
int main(){
//	freopen("testdata.in","r",stdin);
	n=read(),r=read(),alp=2*Pi/n;
	S=0,T=n+n+1;
	fp(i,1,n)x=read(),y=read(),p[i]=point(x,y),cmax(ql,fabs(p[i].len-r)),cmax(qr,p[i].len+r);
	while(qr-ql>eps)ck(mid=(ql+qr)/2)?qr=mid:ql=mid;
	printf("%.8lf\n",ql);
	return 0;
}
posted @ 2019-03-12 11:44  bztMinamoto  阅读(250)  评论(0编辑  收藏  举报
Live2D