[BZOJ5316][计算几何][网络流]JSOI2018:绝地反击

BZOJ5319

一个比较好想的暴力:
二分答案,然后把每艘飞船的能到达的距离算出,是一个圆,把所有圆和攻击轨道求交,相交这段弧和多边形顶点可以匹配,可以发现一定有个多边形顶点在某个交点处
那么就枚举这个顶点,然后 ( O ( n 2 ) ) (O(n^2)) (O(n2))连边,跑 d i n i c dinic dinic O ( e e ) O(e\sqrt e) O(ee )
总复杂度 O ( n 4 l o g n ) O(n^4logn) O(n4logn)

如何优化?我们发现每次转一个角度,每个点都会少一个匹配区间,多一个匹配区间,所以排序后维护退流就可以少一个 n n n

Code:

#include<bits/stdc++.h>
#define eps 1e-9
#define db double
using namespace std;
inline int read(){
	int res=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
	while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
	return res*f;
}
const int N=610,M=100010,inf=1e9;
const db pi=acos(-1);
int n,s,t,pt[N],Head[N],tot,cur[N],d[N],que[N],head,top,flow,cnt;
db rr,de;
struct Edge{int v,nxt,f;}E[M<<1];
struct point{db x,y;}p[N];
db dis(point A){return sqrt(A.x*A.x+A.y*A.y);}
struct data{
	db the;int u,v,t;
	data(db _the=0,int _u=0,int _v=0,int _t=0):the(_the),u(_u),v(_v),t(_t){};
	bool operator <(const data&A)const{return the==A.the?t>A.t:the<A.the;}
}q[N];
inline bool bfs(){
	for(int i=s;i<=t;i++) pt[i]=0,cur[i]=Head[i];
	head=top=0;pt[que[++top]=s]=d[s]=1;  
	while(head<top){
		int u=que[++head];
		for(int i=Head[u];~i;i=E[i].nxt)
			if(E[i].f){
				int v=E[i].v;
				if(pt[v]) continue;
				pt[v]=1;d[v]=d[u]+1;que[++top]=v; 
				if(v==t) return true;
			}
	}
	return false;
}
int dfs(int u,int c){
	if(u==t || !c)return c;
	int flow=0,f;
	for(int i=cur[u];~i;i=E[i].nxt){
		int v=E[cur[u]=i].v;
		if(d[v]==d[u]+1&&(f=dfs(v,min(E[i].f,c)))){
			flow+=f;c-=f;
			E[i].f-=f;E[i^1].f+=f;
			if(!c) break;
		}
	}
	return flow;
}
inline void add(int u,int v){
	E[tot]=(Edge){v,Head[u],1};Head[u]=tot++;
	E[tot]=(Edge){u,Head[v],0};Head[v]=tot++;
}
inline void delta(int u,int v){
	int f=0;
	for(int i=Head[u];~i;i=E[i].nxt)if(E[i].v==v){
		if(!E[i].f)flow--;else f=1;
		E[i].f=E[i^1].f=0;
		break;
	}
	if(f)return;
	for(int i=Head[s];~i;i=E[i].nxt)if(E[i].v==u){E[i].f=1;E[i^1].f=0;break;}
	for(int i=Head[v];~i;i=E[i].nxt)if(E[i].v==t){E[i].f=1;E[i^1].f=0;break;}
	if(bfs()) flow+=dfs(s,inf); 
}
bool check(db mid){
	flow=cnt=tot=0;
	for(int i=s;i<=t;i++) Head[i]=-1;
	for(int i=1;i<=n;i++){
		db d=dis(p[i]);
		if(mid+d<=rr || mid+rr<=d) return false;
		if(rr+d<=mid){
			for(int j=1;j<=n;j++) add(i,j+n);
			continue;
		}
		db the=atan2(p[i].y,p[i].x);
		db delta=acos((d*d+rr*rr-mid*mid)/(2*d*rr));
		db the1=the-delta,the2=the+delta;
		while(the1<0) the1+=pi*2;
		while(the2<0) the2+=pi*2;
		int l=the1/de,r=the2/de;
		the1=the1-de*l;
		the2=the2-de*r;
		l++;r++;
		q[++cnt]=(data){the1,i,l,1};
		q[++cnt]=(data){the2,i,r,-1};
		if(l<=r)for(int j=l+1;j<=r;j++) add(i,j+n);
		else{
			for(int j=1;j<=r;j++) add(i,j+n);
			for(int j=l+1;j<=n;j++) add(i,j+n);
		}
	}
	sort(q+1,q+cnt+1);
	for(int i=1;i<=n;i++) add(s,i),add(i+n,t);
	while(bfs()) flow+=dfs(s,inf);
	if(flow==n) return true;
	for(int i=1;i<=cnt;i++){
		if(~q[i].t){
			add(q[i].u,q[i].v+n);
			if(bfs()) flow+=dfs(s,inf);
			if(flow==n) return true;
		}
		else delta(q[i].u,q[i].v+n);
	}
	return false;
}
int main(){
	n=read(),rr=read();
	de=2*pi/n;s=0,t=n*2+1;
	for(int i=1;i<=n;i++) scanf("%lf%lf",&p[i].x,&p[i].y);
	db l=0,r=200;
	while(r-l>eps){
		db mid=(l+r)/2;
		if(check(mid)) r=mid;
		else l=mid;
	}
	printf("%.8lf\n",l+eps);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值