【计算几何+网络流】BZOJ2864 战火星空

【题目】
原题地址
平面内有 n n n boss \text{boss} boss m m m架飞机,飞机从 ( s x , s y ) (s_x,s_y) (sx,sy) V V V的速度匀速运动到 ( E x , E y ) (E_x,E_y) (Ex,Ey),并在到达终点后消失,飞机子弹有 R R R的射程,有 E E E的能量。一个时刻小飞机可以向任意个 boss \text{boss} boss射击,射击 k k k个将会消耗 k k k个单位的能量,能量消耗完将不能射击。一个 boss \text{boss} boss同时只能受到一架飞机的射击。
求所有 boss \text{boss} boss被攻击总时间最大值。
n , m ≤ 20 n,m\leq 20 n,m20,所有数字为正整数且 &lt; 1000 &lt;1000 <1000

【解题思路】
首先不考虑计算几何的部分,网络流的部分比较简单,就是 S S S到每个飞机连容量,每个能攻击到 boss \text{boss} boss的时间段连 T T T,容量为时间长度,然后由飞机向时间段连边即可。

于是我们只要求出对于每个 boss \text{boss} boss每个飞机能攻击到的时间段即可。

观察到飞机的飞行路线是一条线段,攻击范围是一个半径为 r r r的圆,那么其实可以枚举每个 boss \text{boss} boss,以 boss \text{boss} boss为圆心求出与这条线段的交点,进而求出可攻击时间。

圆和线段求交就可以直接作法向量求出垂足,然后勾股求出距离之后,特判一下方向就可以了,然后再和这个飞机的最短最长时间取一下交即可。

想作死点斜式写个二元一次方程求根也是可以的。

初始旋转坐标系可以避免很多特判。

【参考代码】

#include<bits/stdc++.h>
using namespace std;

typedef double db;
const int N=25,M=N*N*N*N*2;
const int INF=0x3f3f3f3f;
const db eps=1e-10,DNF=(db)1e50,ep=0.1,PI=acos(-1);
int n,m,cnt,nod;
int mp[N][N];
db V[N],R[N],E[N],lim[N];

int read()
{
	int ret=0;char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) ret=ret*10+(c^48),c=getchar();
	return ret;
}

namespace Dinic
{
	int tot,S,T,head[M],cur[M],dis[M];
	queue<int>q;
	struct Tway{int v,nex;db w;}e[M];
	void add(int u,int v,db w)
	{
		//printf("add:%d %d %lf\n",u,v,w);
		e[++tot]=(Tway){v,head[u],w};head[u]=tot;
		e[++tot]=(Tway){u,head[v],0};head[v]=tot;
	}
	bool bfs()
	{
		for(int i=0;i<=nod;++i) dis[i]=-1,cur[i]=head[i];
		while(!q.empty()) q.pop();
		q.push(S);dis[S]=0;
		while(!q.empty())
		{
			int u=q.front();q.pop();
			for(int i=head[u];i;i=e[i].nex)
			{
				int v=e[i].v;
				//printf("%d %d %d %d %lf\n",u,v,dis[u],dis[v],e[i].w);
				if(e[i].w>eps && !~dis[v]) dis[v]=dis[u]+1,q.push(v);
			}
		}
		return ~dis[T];
	}
	db dfs(int x,db flow)
	{
		if(x==T || flow<eps) return flow;
		db used=0,w;
		for(int &i=cur[x];i;i=e[i].nex)
		{
			int v=e[i].v;
			if(dis[v]==dis[x]+1 && (w=dfs(v,min(flow-used,e[i].w)))>eps)
			{
				e[i].w-=w;e[i^1].w+=w;used+=w;
				if(used+eps>=flow) break;
			}
		}
		return used;
	}	
	db dinic(){db res=0;while(bfs()) res+=dfs(S,INF);printf("%.6lf\n",res);}
};
using namespace Dinic;

namespace Geometry
{
	struct node
	{
		db x,y;
		node(){}
		node(db x,db y):x(x),y(y){}
		void turn(db deg)
		{
			deg=deg*PI/180;
			db tx=cos(deg)*x-sin(deg)*y;
			db ty=sin(deg)*x+cos(deg)*y;
			x=tx;y=ty;
		}
		void in(){x=read();y=read();turn(ep);}
		void out(){printf("%lf %lf\n",x,y);}
	}a[N][2],b[N];
	struct line
	{
		node a,b;
		line(){}
		line(node a,node b):a(a),b(b){}
	};
	struct data
	{
		int u,v,w;db pos;
		data(){}
		data(db pos,int u,int v,int w):pos(pos),u(u),v(v),w(w){}
		void out(){printf("%lf %d %d %d\n",pos,u,v,w);}
	}s[M];
	bool cmp(const data&a,const data &b){return a.pos<b.pos;}
	node operator + (const node&a,const node&b){return node(a.x+b.x,a.y+b.y);}
	node operator - (const node&a,const node&b){return node(a.x-b.x,a.y-b.y);}
	node operator * (const node&a,db x){return node(a.x*x,a.y*x);}
	node rotate(const node&a){return node(-a.y,a.x);}
	db getlen(const node&a){return sqrt(a.x*a.x+a.y*a.y);}
	db dot(const node&a,const node&b){return a.x*b.x+a.y*b.y;}
	db css(const node&a,const node&b){return a.x*b.y-a.y*b.x;}
	node linecross(const line&a,const line&b)
	{
		node x=a.a-b.a;
		db t=css(b.b,x)/css(a.b,b.b);
		return a.a+a.b*t;
	}
	node cross(const line&l,const node&p,db v,db r)
	{
		node mid=linecross(l,line(p,rotate(l.b)));
		db t=getlen(mid-p),len;
		if(t+eps>r) return node(DNF,-DNF);
		t=sqrt(r*r-t*t);len=getlen(mid-l.a);
		if(dot(mid-l.a,l.b)<0) len=-len;
		return node((len-t)/v,(len+t)/v);
	}
};
using namespace Geometry;

void init()
{
	n=read();m=read();
	for(int i=1;i<=n;++i) b[i].in();
	for(int i=1;i<=m;++i)
	{
		a[i][0].in();a[i][1].in();
		V[i]=read();R[i]=read();E[i]=read();
		lim[i]=getlen(a[i][1]-a[i][0])/V[i];
	}
	for(int i=1;i<=m;++i)
	{
		line t=line(a[i][0],a[i][1]-a[i][0]);
		for(int j=1;j<=n;++j)
		{
			node t2=cross(t,b[j],V[i],R[i]);
			t2.x=max(t2.x,0.0);t2.y=min(t2.y,lim[i]);
			if(t2.x<t2.y+eps) s[++cnt]=data(t2.x,i,j,1),s[++cnt]=data(t2.y,i,j,-1);
		}
	}
	sort(s+1,s+cnt+1,cmp);
	//for(int i=1;i<=cnt;++i) s[i].out();
}
void getmap()
{
	int cst=0;S=m+1;T=m+2;tot=1;nod=T;
	//printf("S:%d T:%d\n",S,T);
	for(int i=1;i<=m;++i) add(S,i,E[i]);
	for(int i=1;i<=cnt;++i)
	{
		cst+=s[i].w;mp[s[i].u][s[i].v]+=s[i].w;
		if(i^cnt && (s[i].pos+eps<s[i+1].pos))
		{
			if(!cst) continue;
			db t=s[i+1].pos-s[i].pos;
			for(int j=1;j<=n;++j)
			{
				bool fg=0;
				for(int k=1;k<=m;++k) if(mp[k][j])
				{
					if(!fg) add(++nod,T,t),fg=1;
					add(k,nod,INF);
				}
			}
		}
	}
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("BZOJ2864.in","r",stdin);
	freopen("BZOJ2864.out","w",stdout);
#endif
	init();getmap();dinic();
	return 0;
}

【总结】
这是一道写了2.5h的线段与圆交。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值