【LOJ2586】【APIO2018】选圆圈 CDQ分治 扫描线 平衡树

题目描述

  在平面上,有 \(n\) 个圆,记为 \(c_1,c_2,\ldots,c_n\) 。我们尝试对这些圆运行这个算法:

  1. 找到这些圆中半径最大的。如果有多个半径最大的圆,选择编号最小的。记为 \(c_i\)
  2. 删除 \(c_i\) 及与其有交集的所有圆。两个圆有交集当且仅当平面上存在一个点,这个点同时在这两个圆的圆周上或圆内。(原文直译:如果平面上存在一个点被这两个圆所包含,我们称这两个圆有交集。一个点被一个圆包含,当且仅当它位于圆内或圆周上。)
  3. 重复上面两个步骤直到所有的圆都被删除。

  当 \(c_i\) 被删除时,若循环中第1步选择的圆是 \(c_j\) ,我们说 \(c_i\) 被 \(c_j\) 删除。对于每个圆,求出它是被哪一个圆删除的。

  \(n\leq 300000\)

题解

  貌似不太好枚举每个圆,找出剩下的和这个圆相交的圆。

  那就换一种思路。

  枚举每个圆 \(i\),找出第一个与这个圆相交且是作为最大的圆被删掉的圆。

  前面的作为最大的圆被删掉的圆肯定是两两不相交的,且半径大于圆 \(c_i\)

  那么我们可以对前面的圆维护扫描线,每个圆和当前的直线 \(x=x_0\) 相交两次,可以用括号表示 。

  而且由于这些圆两两不相交,括号相对次序不会变。

  由于前面的圆半径都比它大,因此和它有交的圆必然经过 \(x=x_i+r_i\)\(x=x_i-r_i\)\(y=y_i-r_i\)\(y=y_i+r_i\)

  所以我们可以在做扫描线时,查询这四个位置的平衡树上,当前圆的前驱后继。

  但是这道题有很多个询问。

  那就加上一个CDQ分治咯。

  时间复杂度:\(O(n\log^2n)\)

  实际上跑的比 k-d tree 还慢很多倍。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<cmath>
#include<functional>
#include<set>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
void sort(int &a,int &b)
{
    if(a>b)
        swap(a,b);
}
void open(const char *s)
{
#ifndef ONLINE_JUDGE
    char str[100];
    sprintf(str,"%s.in",s);
    freopen(str,"r",stdin);
    sprintf(str,"%s.out",s);
    freopen(str,"w",stdout);
#endif
}
int rd()
{
    int s=0,c,b=0;
    while(((c=getchar())<'0'||c>'9')&&c!='-');
    if(c=='-')
    {
        c=getchar();
        b=1;
    }
    do
    {
        s=s*10+c-'0';
    }
    while((c=getchar())>='0'&&c<='9');
    return b?-s:s;
}
void put(int x)
{
    if(!x)
    {
        putchar('0');
        return;
    }
    static int c[20];
    int t=0;
    while(x)
    {
        c[++t]=x%10;
        x/=10;
    }
    while(t)
        putchar(c[t--]+'0');
}
int upmin(int &a,int b)
{
    if(b<a)
    {
        a=b;
        return 1;
    }
    return 0;
}
int upmax(int &a,int b)
{
    if(b>a)
    {
        a=b;
        return 1;
    }
    return 0;
}
const int N=300010;
const int inf=0x7f7f7f7f;
struct circle
{
    ll x,y,r;
    int id;
};

struct event
{
    ll t;
    int op;
    int v;
    event(){}
    event(ll a,int b,int c)
    {
        t=a;
        op=b;
        v=c;
    }
};

int cmp(circle a,circle b)
{
    if(a.r!=b.r)
        return a.r>b.r;
    return a.id<b.id;
}

int cmp2(event a,event b)
{
    return a.t<b.t;
}

int n;
circle a[N];
int ans[N];
int final[N];
int b[N];
int m;
event c[2*N];
set<pii> s;

int inter(int x,int y)
{
    return (a[x].x-a[y].x)*(a[x].x-a[y].x)+(a[x].y-a[y].y)*(a[x].y-a[y].y)<=(a[x].r+a[y].r)*(a[x].r+a[y].r);
}

void solve(int l,int r)
{
    if(l==r)
    {
        if(ans[l]==inf)
        {
            ans[l]=l;
            b[l]=1;
        }
        return;
    }
    int mid=(l+r)>>1;
    solve(l,mid);
    
    m=0;
    for(int i=l;i<=mid;i++)
        if(b[i])
        {
            c[++m]=event(3*(a[i].x-a[i].r)-2,1,i);
            c[++m]=event(3*(a[i].x+a[i].r),2,i);
        }
    for(int i=mid+1;i<=r;i++)
    {
        c[++m]=event(3*(a[i].x-a[i].r)-1,3,i);
        c[++m]=event(3*(a[i].x+a[i].r)-1,3,i);
    }
    sort(c+1,c+m+1,cmp2);
    for(int i=1;i<=m;i++)
        if(c[i].op==1)
            s.insert(pii(a[c[i].v].y,c[i].v));
        else if(c[i].op==2)
            s.erase(pii(a[c[i].v].y,c[i].v));
        else
        {
            auto it=s.lower_bound(pii(a[c[i].v].y,0));
            if(it!=s.end())
            {
                int x=it->second;
                if(inter(x,c[i].v))
                    ans[c[i].v]=min(ans[c[i].v],x);
            }
            if(it!=s.begin())
            {
                it--;
                int x=it->second;
                if(inter(x,c[i].v))
                    ans[c[i].v]=min(ans[c[i].v],x);
            }
        }
        
        
    m=0;
    for(int i=l;i<=mid;i++)
        if(b[i])
        {
            c[++m]=event(3*(a[i].y-a[i].r)-2,1,i);
            c[++m]=event(3*(a[i].y+a[i].r),2,i);
        }
    for(int i=mid+1;i<=r;i++)
    {
        c[++m]=event(3*(a[i].y-a[i].r)-1,3,i);
        c[++m]=event(3*(a[i].y+a[i].r)-1,3,i);
    }
    sort(c+1,c+m+1,cmp2);
    for(int i=1;i<=m;i++)
        if(c[i].op==1)
            s.insert(pii(a[c[i].v].x,c[i].v));
        else if(c[i].op==2)
            s.erase(pii(a[c[i].v].x,c[i].v));
        else
        {
            auto it=s.lower_bound(pii(a[c[i].v].x,0));
            if(it!=s.end())
            {
                int x=it->second;
                if(inter(x,c[i].v))
                    ans[c[i].v]=min(ans[c[i].v],x);
            }
            if(it!=s.begin())
            {
                it--;
                int x=it->second;
                if(inter(x,c[i].v))
                    ans[c[i].v]=min(ans[c[i].v],x);
            }
        }
        
    solve(mid+1,r);
}

int main()
{
    open("circle");
    scanf("%d",&n);
    ll minx=0x7fffffff,miny=0x7fffffff;
    for(int i=1;i<=n;i++)
    {
//      scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].r);
        a[i].x=rd();
        a[i].y=rd();
        a[i].r=rd();
        a[i].id=i;
        minx=min(minx,a[i].x);
        miny=min(miny,a[i].y);
    }
    for(int i=1;i<=n;i++)
    {
        a[i].x=a[i].x-minx+1;
        a[i].y=a[i].y-miny+1;
    }
    sort(a+1,a+n+1,cmp);
    memset(ans,0x7f,sizeof ans);
    solve(1,n);
    for(int i=1;i<=n;i++)
        final[a[i].id]=a[ans[i]].id;
    for(int i=1;i<=n;i++)
        printf("%d ",final[i]);
    printf("\n");
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ywwyww/p/9083228.html