zoukankan      html  css  js  c++  java
  • 7011. 2021.03.13【2021省赛模拟】nonintersect

    平面上有若干条线段,设其总长为(X)。现在需要把这些线段拆开,点两两匹配成为新的线段,要求这些线段不相交。设其总长为(Y),需要满足(frac{2}{pi}Xle Y)

    (nle 5000)


    考虑(frac{2}{pi})是什么。随机一条直线的倾斜角,一条线段在这个直线上投影的长度和自身长度的比为(|cos heta|),积分一下得到期望(frac{2}{pi})

    根据期望的线性性,随机这条直线之后,所有线段的投影的期望长度和为(frac{2}{pi}X)

    于是必然可以找到一个角度( heta),使得所有线段的投影的期望长度大于等于这个值。(由期望的定义得。否则期望会更小)

    假设找到了这个角度,把点投影到这条线段上,把前后(n)个点分为黑点和白点,互相匹配。匹配之后,投影和的长度肯定大于等于(frac{2}{pi}X)

    不考虑相交时,显然存在完美匹配。现在把边权设为欧几里得距离,此时最小权完美匹配一定无交。于是证明了无交的完美匹配一定存在。

    选择一个(x)坐标最小的点(如果相同(y)坐标最小)拉出来,对其它点极角排序,找到一个和这个点颜色相异的,并且两点连线之后把点集分开成两个黑白点个数相同的点集的点,分治下去做。这样最坏情况下(O(n^2lg n))

    问题是怎么找到( heta):可以发现投影和的长度是关于倾斜角的分段函数,有(O(n))段,每段是(acos x+b sin x)的形式。在每段找出极值即可,最终取最大的即可。这一部分时间(O(nlg n))(段间的分界点需要排序)


    using namespace std;
    #include <bits/stdc++.h>
    #define N 10005
    #define fi first
    #define se second
    #define mp(x,y) make_pair(x,y)
    const double PI=acos(-1);
    int n;
    struct DOT{double x,y;} d[N];
    DOT operator+(DOT a,DOT b){return {a.x+b.x,a.y+b.y};}
    DOT operator-(DOT a,DOT b){return {a.x-b.x,a.y-b.y};}
    double dot(DOT a,DOT b){return a.x*b.x+a.y*b.y;}
    double cro(DOT a,DOT b){return a.x*b.y-a.y*b.x;}
    double len(DOT a){return sqrt(dot(a,a));}
    pair<int,int> ed[N];
    double le[N],theta[N];
    int m;
    pair<double,int> o[N];
    int sign[N];
    double a,b;
    double ans,anss;
    double calc(double x){return a*cos(x)+b*sin(x);}
    void upd(double l,double r){
    	if (calc(l)>anss) anss=calc(l),ans=l;
    	if (calc(r)>anss) anss=calc(r),ans=r;
    	double t=(a!=0?atan(b/a):PI/2);
    	if (l<t && t<r && calc(t)>anss) anss=calc(t),ans=t;
    }
    int c[N];
    pair<double,int> p[N];
    bool cmpang(pair<double,int> a,pair<double,int> b){
    	return a.fi<b.fi || a.fi==b.fi && d[a.se].x<d[b.se].x;
    }
    pair<int,int> ls[N];
    int nl;
    void divide(int q[],int n){
    	if (n==0)
    		return;
    	int v=q[1];
    	for (int i=2;i<=n;++i)
    		if (d[q[i]].x<d[v].x || d[q[i]].x==d[v].x && d[q[i]].y<d[v].y)
    			v=q[i];
    	int k=0;
    	for (int i=1;i<=n;++i)
    		if (q[i]!=v){
    			DOT u=d[q[i]]-d[v];
    			p[++k]=mp(atan2(u.y,u.x),q[i]);
    		}
    	sort(p+1,p+k+1,cmpang);
    	int s=c[v];
    	for (int i=1;i<=k;++i){
    		s+=c[p[i].se];
    		if (s==0){
    			ls[++nl]=mp(v,p[i].se);
    			for (int j=1;j<=k;++j)
    				q[j]=p[j].se;
    			divide(q,i-1);
    			divide(q+i,n-i-1);
    			return;
    		}
    	}
    }
    void doit(){
    	DOT v={cos(ans),sin(ans)};
    	for (int i=1;i<=n*2;++i)
    		p[i]=mp(dot(d[i],v),i);
    	sort(p+1,p+n*2+1);
    	for (int i=1;i<=n;++i) c[p[i].se]=1;
    	for (int i=n+1;i<=n*2;++i) c[p[i].se]=-1;
    	static int q[N];
    	for (int i=1;i<=n*2;++i)
    		q[i]=i;
    	divide(q,n*2);
    }
    int main(){
    //	freopen("nonintersect.in","r",stdin);
    //	freopen("nonintersect.out","w",stdout);
    	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	scanf("%d",&n);
    	for (int i=1;i<=n*2;++i)
    		scanf("%lf%lf",&d[i].x,&d[i].y);
    	for (int i=1;i<=n;++i){
    		int p,q;
    		scanf("%d%d",&p,&q);
    		DOT v=d[p]-d[q];
    		le[i]=len(v);
    		theta[i]=atan2(v.y,v.x);
    		if (theta[i]<0)
    			theta[i]+=PI;
    		if (theta[i]<PI/2){
    			o[++m]=mp(theta[i]+PI/2,i);
    			sign[i]=1;
    		}
    		else{
    			o[++m]=mp(theta[i]-PI/2,i);
    			sign[i]=-1;
    		}
    		a+=le[i]*cos(theta[i])*sign[i];
    		b+=le[i]*sin(theta[i])*sign[i];		
    	}
    	sort(o+1,o+m+1);
    	o[0].fi=0;
    	for (int i=1;i<=n;++i){
    		upd(o[i-1].fi,o[i].fi);
    		sign[o[i].se]*=-1;
    		a+=le[o[i].se]*cos(theta[o[i].se])*sign[o[i].se]*2;
    		b+=le[o[i].se]*sin(theta[o[i].se])*sign[o[i].se]*2;
    	}
    	upd(o[n].fi,PI);
    	doit();
    	for (int i=1;i<=nl;++i)
    		printf("%d %d
    ",ls[i].fi,ls[i].se);
    	return 0;
    }
    
  • 相关阅读:
    MathType编辑半直积符号的步骤
    用几何画板演示涡旋电场的方法
    MathType编辑双向斜箭头的教程
    最实用的几何画板绘图技巧大总结
    怎么让Word编辑公式又快又好
    在几何画板中作三角形高的方法
    MathType中输入破折号的教程
    几何画板5.06最强中文版破解-下载-注册码
    如何通过几何画板来验证海伦公式
    如何用公式编辑器编辑直角三角形符号
  • 原文地址:https://www.cnblogs.com/jz-597/p/14533714.html
Copyright © 2011-2022 走看看