CF97B Superset超级集合

CF97B Superset

这题主要是构造难想。看看数据范围发现连\(O(n^2)\)都被卡了,然后 考试的名称提醒我 想到了分治。

坐标按横坐标为关键字排序后找中间的点进行分治不是点分治qwq

考虑让中间点作一条平行于y轴的直线,让每个点i(mid除外)向这条直线引垂线交于一个点 \(S_i\),那么这个点一定可以加入。

原因:

可行性证明:

考虑不包含 \(S_i\) 的点,由于左半边和右半边都已满足条件,当左边的点i和右边的点j组成矩形时,矩形边缘上一定有2个点\(S_i,S_j\),于是这些S就能使左右合并。

考虑包含 \(S_i\) 的点。如果它和点j组成矩形,那么边缘上一定有点 \(S_j\) ,满足题意。

最优性证明:

对于任意不满足条件的2个点,至少加入1个才能使它们满足条件。而加入的这n/2个点又会有几率不满足,又得加入 n/4 个点……

于是总点数的级别 \(F(n)=n+n/2+n/4+……+1 =F(n\log n)\) ,分治出来的总点数也是这个级别,且都有一定几率会不到nlogn个点,说明分治出来的是一种比较优的情况

考虑更严谨的证明。上面不严谨的主要原因是可能有平行以坐标轴的2点,分治的时候可能多算。如果其中有1个点在分治的时候被作为mid,那么最后去重的时候会去掉。否则在两点之间会多出1个点。如果没有这个点,那么就有可能不满足题意,因此这个点必须加。

分治完了记得去重哦~

由于点数是 \(n\log n\) 的级别,所以数组也要开到 \(n\log n\)

#include<bits/stdc++.h>
using namespace std;
const int N=150005;
int n,len,tot;
struct point{
    int x,y;
}a[N],ans[N],p[N];
bool cmp(const point &a,const point &b)
{
    if(a.x!=b.x)return a.x<b.x;
    else return a.y<b.y;
}
void msort(int l,int r)
{
    if(l==r){ans[++tot]=a[l];return;}
    if(l>r)return;
    int mid=(l+r)>>1;
    msort(l,mid-1);msort(mid+1,r);
    for(int i=l;i<=r;++i)
    {
        point tmp;
        tmp.x=a[mid].x;
        tmp.y=a[i].y;
        ans[++tot]=tmp;
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        scanf("%d%d",&a[i].x,&a[i].y);
    sort(a+1,a+n+1,cmp);
    msort(1,n);
    sort(ans+1,ans+tot+1,cmp);
    ans[0].x=ans[0].y=1000000007;
    for(int i=1;i<=tot;++i)
    {
        if(ans[i].x!=ans[i-1].x||ans[i].y!=ans[i-1].y)
            p[++len]=ans[i];
    }
    printf("%d\n",len);
    for(int i=1;i<=len;++i)
        printf("%d %d\n",p[i].x,p[i].y);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzctommy/p/12350334.html
97