Bzoj1822:[JSOI2010]Frozen Nova 冷冻波:计算几何+网络流

13 篇文章 0 订阅
4 篇文章 0 订阅

题目链接:[JSOI2010]Frozen Nova 冷冻波

二分答案,把最优性问题转换为判定性问题

对于判断树木是否与线段相交,分两种情况讨论:

1:圆心作线段的垂线垂足不在线段上

2:圆心作线段的垂线垂足在线段上

对于1,直接比较圆心与线段两个端点距离的最小值是否小于半径

对于2,算出垂线长度后比较

区分1、2两种情况用点积即可,相当于间接判断cos的值得正负

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=251;
const int inf=0x7fffffff/3;
struct Lich{double x,y,r;int t;}l[maxn];
struct point{double x,y;};
struct Spirit{double x,y;}spr[maxn];
struct Tree{double x,y,r;}t[maxn];
struct edge{int to,next,w;}G[maxn*maxn*2];
int tot=0,n,m,tr,h[10000],vis[10000],mx=0;
int S,T,cur[10000],mp[maxn][maxn],mark[maxn];

void add(int x,int y,int z){
	G[++tot].to=y;G[tot].next=h[x];h[x]=tot;G[tot].w=z;
	G[++tot].to=x;G[tot].next=h[y];h[y]=tot;G[tot].w=0;
}

double powe(double x){return x*x;}

double qdis(Lich a,Spirit b){
	return sqrt(powe(a.x-b.x)+powe(a.y-b.y));
}

double qdis2(double a,double b,double c,double d){
	return sqrt(powe(a-c)+powe(b-d));
}

double cross(point a,point b,point c){
	double x1=b.x-a.x,y1=b.y-a.y;
	double x2=c.x-a.x,y2=c.y-a.y;
	return x1*y2-x2*y1;
}

double dot(point a,point b,point c){
    double x1=b.x-a.x,y1=b.y-a.y;
	double x2=c.x-a.x,y2=c.y-a.y;
	return x1*x2+y1*y2;
}

bool in_cir(Lich a,Spirit b,Tree c){
	point tmp1,tmp2,tmp3;
	tmp1=(point){a.x,a.y};
	tmp2=(point){b.x,b.y};
	tmp3=(point){c.x,c.y};
	if (dot(tmp3,tmp1,tmp2)>=0) return min(qdis2(a.x,a.y,c.x,c.y),qdis2(b.x,b.y,c.x,c.y))<=c.r;
	else return abs(cross(tmp1,tmp3,tmp2))/qdis(a,b)<=c.r;
}

bool bfs(){
	for (int i=S;i<=T;++i) vis[i]=-1;
	queue<int>q; q.push(S); vis[S]=0;
	while (!q.empty()){
		int u=q.front(); q.pop();
		for (int i=h[u];i;i=G[i].next){
			int v=G[i].to;
			if (vis[v]==-1&&G[i].w)
			    vis[v]=vis[u]+1,q.push(v);
		}
	}return vis[T]!=-1;
}

int dfs(int x,int f){
	if (!f||x==T) return f;
	int w,used=0;
	for (int i=cur[x];i;i=G[i].next)
	    if (vis[G[i].to]==vis[x]+1){
			w=f-used;
			w=dfs(G[i].to,min(w,G[i].w));
			G[i].w-=w; G[i^1].w+=w;
			used+=w; if (G[i].w) cur[x]=i;
			if (used==f) return used;
	    }
	if (!used) vis[x]=-1;
	return used;
}

int dinic(){
	int ret=0;
	while(bfs()){
		for (int i=S;i<=T;++i) cur[i]=h[i];
		ret+=dfs(S,inf);
	}return ret;
}

bool check(int x){
	tot=1;
	for (int i=S;i<=T;++i) h[i]=0;
	for (int i=1;i<=n;++i){
		int tmp=x/l[i].t+1;
		add(S,i,tmp);
	}
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=m;++j)
	        if (mp[i][j]) add(i,j+n,1);
	for (int i=1;i<=m;++i) add(i+n,T,1);
	int sum=dinic();
	return sum==m;
}

void solve(){
	int l=0,r=m*mx,ans=inf;
	while (l<=r){
		int mid=(l+r)>>1;
		if (check(mid)) r=mid-1,ans=min(ans,mid);
		else l=mid+1;
	}printf("%d\n",ans==inf?-1:ans);
}

int main(){
	scanf("%d%d%d",&n,&m,&tr);
	S=0; T=n+m+1;
	for (int i=1;i<=n;++i) scanf("%lf%lf%lf%d",&l[i].x,&l[i].y,&l[i].r,&l[i].t);
	for (int i=1;i<=n;++i) mx=max(mx,l[i].t);
	for (int i=1;i<=m;++i) scanf("%lf%lf",&spr[i].x,&spr[i].y);
	for (int i=1;i<=tr;++i) scanf("%lf%lf%lf",&t[i].x,&t[i].y,&t[i].r);
	for (int i=1;i<=n;++i)
	    for (int j=1;j<=m;++j){
			double dist=qdis(l[i],spr[j]);
			if (dist>l[i].r) continue;
			bool flag=1;
			for (int k=1;k<=tr;++k)
			    if(in_cir(l[i],spr[j],t[k])){flag=0;break;}
			if (flag) mp[i][j]=1,mark[j]=1;
	    }
	for (int i=1;i<=m;++i)
		if (!mark[i]){printf("-1");return 0;}
	solve();
}



  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值