[BZOJ4206]最大团(计算几何+dp)

来源:互联网 发布:自选股软件下载 编辑:程序博客网 时间:2024/06/05 12:16

题目描述

传送门

题解

非二分图的最大团问题是npc hard(我不会告诉你我刚知道)
所以今天gang了半天是毫无意义的(逃

一个非常奇妙的转化
首先圆内和圆上的点扔掉
对于每一个点做它的两条切线,每个点的两个切点在圆上会形成一段连续的弧,两个点不能同时选,当且仅当两个弧相离或包含
栗子:
这里写图片描述
这里写图片描述
这两种情况都是不行的
将圆展成一条链,跨越端点的线段就改为它的补集
这样问题就变成了在一坨线段里选一些没有相离或包含关系的线段,并且数量最多
将线段左端点排序,然后就变成了求关于右端点的最长上升子序列
但是不能相离?
强制一个线段选,做n次,这样求最长上升子序列的过程用二分优化一下
时间复杂度O(n2logn)

第一次在bz上rk1,超鸡冻

代码

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>#include<cmath>using namespace std;const double pi=acos(-1.0);const double inf=1e18;int n,tmp,ans;double R,x,y,len,ang1,ang2,stack[2005];struct data{double x,y;bool flag;}s[2005];int cmp(data a,data b){    return a.flag<b.flag||(a.flag==b.flag&&a.x<b.x)||(a.flag==b.flag&&a.x==b.x&&a.y<b.y);}int find(double x){    int l=0,r=tmp,mid,ans=0;    while (l<=r)    {        mid=(l+r)>>1;        if (stack[mid]>=x) ans=mid,r=mid-1;        else l=mid+1;    }    return ans;}int main(){    scanf("%d%lf",&n,&R);    for (int i=1;i<=n;++i)    {        scanf("%lf%lf",&x,&y);        len=sqrt(x*x+y*y);        if (len<=R)        {            s[i].flag=1;            continue;        }        ang1=atan2(y,x);ang2=acos(R/len);        s[i].x=ang1-ang2;s[i].y=ang1+ang2;        if (s[i].y>pi) s[i].y-=2*pi,swap(s[i].x,s[i].y);        if (s[i].x<-pi) s[i].x+=2*pi,swap(s[i].x,s[i].y);    }    sort(s+1,s+n+1,cmp);    for (int i=1;i<=n;++i)        if (s[i].flag) {n=i-1;break;}    stack[0]=-inf;    for (int i=1;i<=n;++i)    {        if (s[i].flag) break;        stack[tmp=1]=s[i].y;        for (int j=i+1;j<=n;++j)            if (s[j].x>s[i].y) break;            else            {                if (s[j].y>stack[tmp])                {                    stack[++tmp]=s[j].y;                    continue;                }                int t=find(s[j].y);                if (t!=1) stack[t]=s[j].y;            }        ans=max(ans,tmp);    }    printf("%d\n",ans);}
0 0