2018.03.08【JSOI2018】【洛谷P4518】【BZOJ5316】绝地反击(二分图匹配)(退流)(扫描线)

洛谷传送门

BZOJ传送门


解析:

我们首先二分答案。

然后判断如果一条飞船在时限内甚至飞不到圆周上就肯定gg了。

如果能的话,我们将圆周分为 n n n等份,第一份是从 x x x轴开始的 1 / n 1/n 1/n圆,我们看这条飞船能不能飞到整个圆,如果能,就和每个区间连一条边。

如果不能我们将它和整个圆匹配,就先记录哪些区间的端点是能够匹配的,然后记录下匹配的最左端点和未匹配的最左端点。

跑二分图最大匹配,如果 n n n个位置全部能匹配的话,答案合法。

否则,我们开始将这个圆旋转,显然,旋转 α \alpha α弧度和旋转 α + 2 π n \alpha+\frac{2\pi}n α+n2π弧度是没有区别的,我们将刚才记录的所有左端点记录一下,上极角序扫描线,然后看旋转过程中那些位置失去了匹配资格,哪些端点拥有了匹配资格。

对于失去匹配资格的,我们在二分图上进行退流操作,对于拥有了匹配资格的,就加边后再在残量网络上跑一次最大流更新匹配数。

正确性比较显然,也比较好写,目前拿到BZOJrk1和洛谷rk1


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		re bool f=0;
		while(!isdigit(c=gc()))if(c=='-')f=1;re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return f?-num:num;
	}
}
using namespace IO;

cs int N=605;

struct edge{
	int to,rev,cap;
	edge(cs int &_to,cs int &_rev,cs int &_cap):to(_to),rev(_rev),cap(_cap){}
};

vector<edge> G[N];
inline void addedge(int u,int v,int val){
	G[u].push_back(edge(v,G[v].size(),val));
	G[v].push_back(edge(u,G[u].size()-1,0));
}
int S,T;
int lev[N];
vector<edge>::iterator cur[N];
inline bool BFS(){
	memset(lev,-1,sizeof(int)*(T+1));
	for(int re i=0;i<=T;++i)cur[i]=G[i].begin();
	queue<int,list<int> > q;
	q.push(S),lev[S]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(vector<edge>::iterator e=G[u].begin();e!=G[u].end();++e){
			if(e->cap&&lev[e->to]==-1){
				lev[e->to]=lev[u]+1;
				if(e->to==T)return true;
				q.push(e->to);
			}
		}
	}
	return false;
}

inline int Dinic(cs int &u,cs int &flow){
	if(u==T)return flow;
	int ans=0;
	for(vector<edge>::iterator &e=cur[u];e!=G[u].end();++e){
		if(e->cap&&lev[e->to]>lev[u]){
			int delta=Dinic(e->to,min(flow-ans,e->cap));
			e->cap-=delta;G[e->to][e->rev].cap+=delta;
			if((ans+=delta)==flow)return ans;
		}
	}
	lev[u]=-1;
	return ans;
}

int flow;
inline void Flow(){while(BFS())flow+=Dinic(S,0x3f3f3f3f);}

inline void pop_flow(int u,int v){
	vector<edge>::iterator it;
	bool flag;
	for(it=G[u].begin();it!=G[u].end();++it)
	if(it->to==v){
		flag=it->cap;
		it=G[u].erase(it);
		break;
	}
	for(;it!=G[u].end();++it)G[it->to][it->rev].rev--;
	for(it=G[v].begin();it!=G[v].end();++it)
	if(it->to==u){
		it=G[v].erase(it);
		break;
	}
	for(;it!=G[v].end();++it)G[it->to][it->rev].rev--;
	if(flag)return ;
	--flow;
	for(it=G[S].begin();it!=G[S].end();++it)
	if(it->to==u){
		it->cap^=1;
		G[u][it->rev].cap^=1;
		break;
	}
	for(it=G[T].begin();it!=G[T].end();++it)
	if(it->to==v){
		it->cap^=1;
		G[v][it->rev].cap^=1;
		break;
	}
	Flow();
}

cs double PI=acos(-1);
cs double eps=1e-6;
struct Point{
	double x,y;
	Point(){}
	Point(cs double &_x,cs double &_y):x(_x),y(_y){}
	friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
	friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
	friend double operator*(cs Point &a,cs Point &b){return a.x*b.y-a.y*b.x;}
	double norm()cs{return sqrt(x*x+y*y);}
}p[N];

struct data{
	double t;
	int u,v,op;
	data(){}
	data(cs double &_t,cs int &_u,cs int &_v,cs int &_op):t(_t),u(_u),v(_v),op(_op){}
	friend bool operator<(cs data &a,cs data &b){
		return a.t==b.t?a.op>b.op:a.t<b.t;
	}
}q[N];

int cnt,n;
double radius,block;

inline bool check(double X){
	cnt=0;flow=0;
	for(int re i=0;i<=T;++i)G[i].clear();
	for(int re i=1;i<=n;++i){
		double len=p[i].norm();
		if(fabs(radius-len)>X)return false;
		if(len+radius<=X)for(int re j=1;j<=n;++j)addedge(i,j+n,1);
		else {
			double re ang1=atan2(p[i].y,p[i].x);
			double re ang2=acos((radius*radius+len*len-X*X)/(2*radius*len));
			double re al=ang1-ang2,ar=ang1+ang2;
			while(al<0)al+=2*PI;
			while(ar<0)ar+=2*PI;
			int l=al/block,r=ar/block;
			q[++cnt]=data(al-l*block,i,l+1+n,1);++l;
			q[++cnt]=data(ar-r*block,i,r+1+n,0);++r;
			if(l<=r)for(int re j=l+1;j<=r;++j)addedge(i,j+n,1);
			else {
				for(int re j=1;j<=r;++j)addedge(i,j+n,1);
				for(int re j=l+1;j<=n;++j)addedge(i,j+n,1);
			}
		}
	}
	for(int re i=1;i<=n;++i)addedge(S,i,1),addedge(i+n,T,1);
	Flow();
	if(flow==n)return true;
	sort(q+1,q+cnt+1);
	for(int re i=1;i<=cnt;++i){
		switch(q[i].op){
			case 1:{
				addedge(q[i].u,q[i].v,1);
				Flow();
				if(flow==n)return true;
				break;
			}
			case 0:pop_flow(q[i].u,q[i].v);break;
		}
	}
	return false;
}

double l,r=200;
signed main(){
	n=getint();radius=getint();
	block=2*PI/n;
	S=0,T=2*n+1;
	for(int re i=1;i<=n;++i){
		p[i].x=getint();
		p[i].y=getint();
	}
	while(l+1e-6<=r){
		double mid=(l+r)/2;
		if(check(mid))r=mid;
		else l=mid;
	}
	printf("%.8f",l);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值