tkj
文章143
标签102
分类0
[CEOI2012]Printed Circuit Board

[CEOI2012]Printed Circuit Board

.

题意:给出一个简单多边形,问有多少个点能在原点被看见。
题解:set
先定义next(x)为x的下一个点,prev(x)为x的上一个点,multi(p0,p1,p2)为叉积。假设点的顺序是顺时针,如图所示。

很明显,只需要从右往左的边,也就是multi(O,x,next(x))>0的边,就可以挡住所有的点;换言之,只有这些边上的点有可能被看见。还是简单证明一下吧:对于上面的边(细边)它们已经被下面的边完全挡住了,不用考虑;对于一条在下面的从左往右的边,要从first走到它的起点——也就是左边的点,一定会先越过它;而从它的终点——也就是右边的点,要走到last,也一定会越过它。这两部分一个从这条边上面越过,另一部分从下面越过,一定可以覆盖这条边。
所以我们只需要把所有点按极角排序,从first逆时针扫到last,每个点作为以这个点为起点的边(一定是从右往左的边)插入一个可以插入元素、维护最小值、删除元素的数据结构中(线段树、堆、平衡树都可以实现。我用了set),删除以这个点为终点的边(假如在这个数据结构里的话),每次看看当前点是不是最靠下的(没有被挡住的)。
注意点也是可以挡住视线的,所以最后要处理一下极角相同,也就是与原点在同一条直线上的点,除了最近的,把剩下的都标为看不见就好了。
代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
using namespace std;

long long n,a[200010];
struct pnt
{
    long long x,y;
}p[200010],o;
bool ans[200010],rev=0;

long long next(long long x)
{
    return x==n?1:x+1;
}
long long prev(long long x)
{
    return x==1?n:x-1;
}
long long multi(pnt p0,pnt p1,pnt p2)
{
    long long x1=p1.x-p0.x,y1=p1.y-p0.y,x2=p2.x-p0.x,y2=p2.y-p0.y;
    return x1*y2-x2*y1;
}
long long cmp(long long x,long long y)
{
    return multi(o,p[x],p[y])==0?p[x].x<p[y].x:multi(o,p[x],p[y])>0;
}
void pre()
{
    p[0]=p[n];
    p[n+1]=p[1];
    long long st=1;
    for(long long i=2;i<=n;i++)
    {
        if(multi(o,p[st],p[i])<0||multi(o,p[st],p[i])==0&&p[i].x<p[st].x)
        st=i;
    }
    if(multi(p[st],p[next(st)],p[prev(st)])>0)
    reverse(p,p+2+n),rev=1,st=n-st+1;
    for(long long i=1;i<=n;i++)
    {
        a[i]=i;
    }
    sort(a+1,a+n+1,cmp);
}
struct hhh
{
    long long x;
    hhh(long long _x)
    {
        x=_x;
    }
    bool operator<(hhh y)const
    {
        if(x==y.x)
        return 0;
        return multi(o,p[x],p[y.x])>=0&&multi(p[x],p[next(x)],p[y.x])<=0||multi(o,p[x],p[y.x])<0&&multi(p[y.x],p[next(y.x)],p[x])>0;
    }
};
void out()
{
    if(rev)
    reverse(ans+1,ans+1+n);
    vector<int>hh;
    for(long long i=1;i<=n;i++)
    if(ans[i])
    hh.push_back(i);
    printf("%lld\n",hh.size());
    for(vector<int>::iterator it=hh.begin();it!=hh.end();it++)
    printf("%lld ",*it);
}
set<hhh>s;
bool in[200010];
int main()
{
    o={0,0};
    scanf("%lld",&n);
    for(long long i=1;i<=n;i++)
    scanf("%lld%lld",&p[i].x,&p[i].y);
    pre();
    for(long long i=1;i<=n;i++)
    {
        if(in[prev(a[i])])
        {
            if(s.begin()->x==prev(a[i]))
            ans[a[i]]=1;
            in[prev(a[i])]=0;
            set<hhh>::iterator it=s.lower_bound(hhh(prev(a[i])));
//            it--;
//            printf("%lld %lld\n",it->x,prev(a[i]));
            s.erase(it);
        }
        if(multi(o,p[a[i]],p[next(a[i])])>0)//只有这些边是有用的 
        {
            s.insert(hhh(a[i]));
            in[a[i]]=1;
            long long uu=s.begin()->x;
            if(multi(p[uu],p[next(uu)],p[a[i]])==0)
            ans[a[i]]=1;
        }
        else if(multi(p[s.begin()->x],p[next(s.begin()->x)],p[a[i]])==0)
        ans[a[i]]=1;/*
        printf("%lld\n",a[i]);
        for(long long i=1;i<=n;i++)
        printf("%lld ",in[i]);
        puts("");
        print();*/
    }
    ans[a[n]]=1;
    for(long long i=1,j=1;i<=n;i=j)
    {
        j++;
        while(j<=n&&multi(o,p[a[i]],p[a[j]])==0)
        ans[a[j++]]=0;
    }
    out();
}
本文作者:tkj
本文链接:https://tkj666.github.io/89/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可