zoukankan      html  css  js  c++  java
  • [JZOJ3297] 【SDOI2013】逃考

    题目

    我发现我现在连题面都懒得复制粘贴了……

    题目大意

    在一个矩形中有一堆点,这堆点按照以下规则将矩形瓜分成一堆块:
    对于每个坐标,它属于离它最近的点的块。
    一个人从某个坐标出发到矩形外面,求经过的最少的区域个数。


    思考历程

    显然,如果将每个点管辖的区域处理出来,和周围接壤的区域连一条边,答案就是一个最短路的事情。
    可问题是,怎么求啊?!
    作为计算几何白痴,我不得不弃疗……


    正解

    正解是求半平面交……
    显然,对于每个点,枚举其它点,作两点连线的垂直平分线,围成的区域就是它管辖的范围。
    所以这就是半平面交的裸题……
    首先要有一堆计算几何的知识,向量的基本操作(点积、叉积)、求交点、点到直线距离……
    先把这些打一遍。
    然后我们求出每条直线的解析式(当然是向量表示),按照极角排序(就是直线和xx轴的夹角),如果两条直线的极角一样就选离点更近的那一条。
    接着用一个双向队列,有直线加进来时,看看队尾和队尾的前一条直线的交点是否在这条直线的外面,如果是,就将队尾踢掉。循环操作。
    同时队头也要类似地操作。
    在搞完之后试着用队头弹掉不合法的队尾。
    于是半平面交就处理出来了……
    将残留的直线的另一边的区域和自己连边,然后跑最短路。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define N 610
    #define PI 3.1415926535
    int n;
    double X,Y;
    struct DOT{
    	double x,y;
    } d[N],beg;
    int nl;
    struct Line{
    	DOT p,v;
    } l[N];
    inline DOT operator+(const DOT &a,const DOT &b)
    {return {a.x+b.x,a.y+b.y};}
    inline DOT operator-(const DOT &a,const DOT &b)
    {return {a.x-b.x,a.y-b.y};}
    inline DOT operator*(const DOT &a,const double b)
    {return {a.x*b,a.y*b};}
    inline double dot(const DOT &a,const DOT &b)
    {return a.x*b.x+a.y*b.y;}
    inline double cro(const DOT &a,const DOT &b)
    {return a.x*b.y-a.y*b.x;}
    inline double len(const DOT &a)
    {return sqrt(dot(a,a));}
    inline double len2(const DOT &a)
    {return dot(a,a);}
    inline bool direct(const DOT &a,const DOT &b)
    {return cro(a,b)<=0;}
    inline DOT intersect(const Line &a,const Line &b)
    {return b.p+b.v*(cro(a.v,a.p-b.p)/cro(a.v,b.v));}
    inline double distl(const Line &a,const DOT b)
    {return abs(cro(b-a.p,a.v)/len(a.v));}
    int with[N];
    double theta[N];
    double LEN2[N];
    int p[N];
    inline bool cmpp(int a,int b)
    {return theta[a]<theta[b] || theta[a]==theta[b] && LEN2[a]<LEN2[b];}
    int q[N];
    struct EDGE{
    	int to;
    	EDGE *las;
    } e[N*N];
    int ne;
    EDGE *last[N];
    inline void link(int u,int v){
    	e[++ne]={v,last[u]};
    	last[u]=e+ne;
    }
    int dis[N];
    inline void BFS(int S){
    	int head=0,tail=1;
    	q[1]=S;
    	memset(dis,0,sizeof dis);
    	dis[S]=1;
    	do{
    		int x=q[++head];
    		for (EDGE *ei=last[x];ei;ei=ei->las)
    			if (!dis[ei->to]){
    				dis[ei->to]=dis[x]+1;
    				if (ei->to==0){
    					printf("%d
    ",dis[ei->to]-1);
    					return;
    				}
    				q[++tail]=ei->to;
    			}
    	}
    	while (head!=tail);
    }
    int main(){
    	freopen("in.txt","r",stdin);
    	int T;
    	scanf("%d",&T);
    	while (T--){
    		scanf("%d%lf%lf%lf%lf",&n,&X,&Y,&beg.x,&beg.y);
    		if (n==0){
    			printf("0
    ");
    			continue;
    		}
    		for (int i=1;i<=n;++i)
    			scanf("%lf%lf",&d[i].x,&d[i].y);
    		ne=0;
    		memset(last,0,sizeof last);
    		for (int i=1;i<=n;++i){
    			l[1]={X,0,0,1};
    			l[2]={X,Y,-1,0};
    			l[3]={0,Y,0,-1};
    			l[4]={0,0,1,0};
    			with[1]=with[2]=with[3]=with[4]=0;
    			nl=4;
    			for (int j=1;j<=n;++j)
    				if (i!=j){
    					DOT mid={(d[i].x+d[j].x)/2,(d[i].y+d[j].y)/2},t=mid-d[i];
    					l[++nl]={mid,-t.y,t.x};
    					with[nl]=j;
    				}
    			for (int j=1;j<=nl;++j){
    				theta[j]=atan2(l[j].v.y,l[j].v.x);
    				if (theta[j]<0)
    					theta[j]+=PI*2;
    				LEN2[j]=distl(l[j],d[i]);
    				p[j]=j;
    			}
    			sort(p+1,p+nl+1,cmpp);
    			int tmp=nl;
    			nl=0;
    			for (int j=1;j<=tmp;++j)
    				if (j==1 || theta[p[j]]!=theta[p[j-1]])
    					p[++nl]=p[j];
    			int head=1,tail=0;
    			for (int j=1;j<=nl;++j){
    				while (head<tail && direct(l[p[j]].v,intersect(l[q[tail-1]],l[q[tail]])-l[p[j]].p)==1)
    					--tail;
    				while (head<tail && direct(l[p[j]].v,intersect(l[q[head]],l[q[head+1]])-l[p[j]].p)==1)
    					++head;
    				q[++tail]=p[j];
    			}
    			while (head<tail && direct(l[q[head]].v,intersect(l[q[tail-1]],l[q[tail]])-l[q[head]].p)==1)
    				--tail;
    			for (int j=head;j<=tail;++j)
    				link(i,with[q[j]]);
    		}
    		int mni=1;
    		for (int i=2;i<=n;++i)
    			if (len2(d[i]-beg)<len2(d[mni]-beg))
    				mni=i;
    		BFS(mni);
    	}
    	return 0;
    }
    

    总结

    必须要恶补计算几何
    在打计算几何之前,先把一堆模板打下来,这样会方便很多。

  • 相关阅读:
    随笔:我为什么要写博客?
    用纯C语言写的一个植物大战僵尸的外挂
    方法:如何获取操作系统所有分区(逻辑驱动器)
    Srping syntactically incorrect.错误记录
    重建项目报错
    easyui datagrid 跨页选择
    CentOS 6编译安装ipvsadm和keepalived
    CentOS 6下ActiveMQ 5.5安装及使用MySQL
    extjs 4中TreePanel和GridPanel使用
    Linux安装性能问题
  • 原文地址:https://www.cnblogs.com/jz-597/p/11145214.html
Copyright © 2011-2022 走看看