题解 P3997 【[SHOI2013]扇形面积并】

· · 题解

平衡树+扫描线

用平衡树维护kth

一个角度上覆盖k次所能取到的最大半径便是该角度上覆盖的所有半径中的第k大

比如角度2上覆盖着半径为1926,817,666的扇形,则当k=2时,角度2最大能取到817

维护第k大,想到平衡树

扇形转线段

把每个扇形看作2/4条线段,每条线段的长度是它的半径

用三元组(len,pos,tag)表示一条线段,len就是线段的长度也就是它所对应的扇形的半径,pos表示该线段所处的角度,tag=0表示该线段要被插入,tag=1表示要从平衡树中删除一条长度为len的线段

把len当作平衡树的排序原则,把pos作为线段排序的原则,把所有的线段存下来后,按照极坐标角度从小到大排序

负角度转正

那么有负角度,转成正角度更好做,如图所示是当m=8时的极坐标系

0同时又可以是2m,一个角度p(p<0),转成正后便是2m+p,比如p=-3,对应m=8时2*8-3=13

考虑怎么把扇形分解成线段

对于一个以len为半径,从角度l覆盖到r的扇形,题目并没有保证l<r,所以要进行一波分类讨论

1.l==r (好像数据没有这种情况)

这种情况便是一个圆,转为(len,0,0)和(len,2m,1)即可,表示一个圆

2.l<r,且l≠0,r≠0

1)l>0,r>0

直接转为(len,l,0),(len,r,1)即可

2)l>0,r<0

不满足l<r的前提,舍去

3)l<0,r>0

此时该扇形会覆盖到角度0,也就是l对应的正数是是一个>m的数,r则<m,直接塞(len,l,0),(len,r,1)则会出现问题,因为扫描线是按极坐标角度从小到大扫的,而这样塞l会被排在r后面

所以考虑以0角度为基准,把该扇形分割为[0,l],[0,r]两部分

对于0到l的这一块,分解为(len,2m+l,0),(len,2m,1)

对于0到r的这一块,分解为(len,0,0),(len,r,1)

4)l<0,r<0

l,r同为负,转成2m+l,2m+r即可

转为(len,2m+l,0),(len,2m+r,1)

3.l>r

1)l>0,r>0

同样分割成两部分,转为(len,l,0),(len,2m,1),(len,0,0),(len,r,1)

2)l>0,r<0

r转正变成2m+r,转为(len,l,0),(len,2m+r,1)

3)l<0,r>0

不满足l>r的前提,舍去

4)l<0,r<0

同理分解为(len,2m+l,0),(len,2m,1),(len,0,0),(len,2m+r,1)

4.l==0,r≠0 (由于可能有0的存在导致在2.3.的时候懒得去想什么地方写>=0什么地方写>0,所以干脆把有0的列出来好了)

1)r<0 (len,0,0),(len,2m+r,1)
2)r>0 (len,0,0),(len,r,1)

5.l≠0,r==0

1)l<0 (len,2m+l,0),(len,2m,1)
2)l>0 (len,l,0),(len,2m,1)

如何扫描线

扇形分解成线段了之后,按照pos为关键字sort一下,从小到大遍历一遍

一个半径为r,圆心角为α的扇形的扇形对答案的贡献为*αr²**

循环中一开始先累计答案:*ans+=(当前平衡树中第k大长度)²(s[i].pos-s[i-1].pos)**,表示前一个位置到当前位置的区域的面积

然后看当前线段的标记tag

tag为0时往平衡树中插入线段长len

tag为1时在平衡树中删去一个len

代码:指针Splay(记得处理好long long)

#include<cstdio>
#include<algorithm>
using std::sort;
template<class type>inline void read(type &in)
{
    in=0;char ch=getchar();short fh=1;
    while (ch<48||ch>57)fh=ch=='-'?-1:fh,ch=getchar();
    while (ch>47&&ch<58)in=(in<<3)+(in<<1)+ch-48,ch=getchar();
    in*=fh;
}
typedef long long ll;
const int N=1e5+10;
class Splay
{
    private:
        struct tree
        {
            int size,value,cnt;
            tree *son[2],*fa;
            inline const void pushup()
            {
                size=son[0]->size+cnt+son[1]->size;
            }
            inline const bool identity()
            {
                return fa->son[1]==this;
            }
        }*null,*root,memory_pool[N<<2],*tail,*rec[N<<2];
        int top;
        inline const void init()
        {
            tail=memory_pool;
            null=tail++;
            null->size=null->cnt=null->value=0;
            null->son[0]=null->son[1]=null->fa=null;
            root=null;
        }
        inline tree *spawn(int key)
        {
            tree *p=top?rec[--top]:tail++;
            p->value=key;
            p->size=p->cnt=1;
            p->fa=p->son[0]=p->son[1]=null;
            return p;
        }
        inline const void erase(tree *&p)
        {
            rec[top++]=p;p=null;
        }
        inline const void connect(tree *p,tree *fa,bool which)
        {
            if (p!=null)p->fa=fa;
            if (fa!=null)fa->son[which]=p,fa->pushup();
        }
    protected:
        inline const void rotate(tree *p)
        {
            tree *fa=p->fa;
            bool id=p->identity();
            connect(p,fa->fa,fa->identity());
            connect(p->son[id^1],fa,id);
            connect(fa,p,id^1);
        }
        inline const void splay(tree *p)
        {
            for (tree *fa;(fa=p->fa)!=null;rotate(p))
                if (fa->fa!=null)
                    rotate(p->identity()^fa->identity()?p:fa);
            root=p;
        }
        inline const void find(int key)
        {
            tree *now=root;
            while (now->son[key<now->value]!=null&&now->value!=key)
                now=now->son[key<now->value];
            splay(now);
        }
        inline tree *precursor(tree *p)
        {
            splay(p);
            tree *now=p->son[0];
            while (now->son[1]!=null)now=now->son[1];
            return now;
        }
    public:
        inline Splay(){init();}
        inline const void insert(int key)
        {
            if (root==null)return (void)(root=spawn(key));
            tree *now=root;
            while (1)
            {
                if (now->value==key)
                    return now->cnt++,now->pushup(),splay(now);
                tree *fa=now;
                bool which=key<fa->value;
                now=fa->son[which];
                if (now==null)
                    return now=spawn(key),connect(now,fa,which),splay(now);
            }
        }
        inline const void Delete(int key)
        {
            find(key);tree *p=root;
            if (p->cnt>1)return p->cnt--,p->pushup();
            if (p->son[0]==null&&p->son[1]==null)return root=null,erase(p);
            if (p->son[0]==null)return (root=p->son[1])->fa=null,erase(p);
            if (p->son[1]==null)return (root=p->son[0])->fa=null,erase(p);
            tree *pre=precursor(p);splay(pre);connect(p->son[1],pre,1);erase(p);
        }
        inline const int findrank(int k)
        {
            tree *now=root;
            while (1)
                if (k<=now->son[0]->size)now=now->son[0];
                else 
                    if ((k-=now->son[0]->size+now->cnt)<=0)
                        return now->value;
                    else now=now->son[1];
        }
        inline const int size()
        {
            return root->size;
        }
}S;
int n,m,k,cnt;
ll ans;
struct segment
{
    int len,pos;bool tag;
    inline const bool operator<(const segment &p)const
    {
        return pos<p.pos;
    }
}s[N<<2];
inline const void addseg(int len,int pos,bool tag)
{
    s[++cnt].len=len;s[cnt].pos=pos;s[cnt].tag=tag;
}
inline const ll square(int x)
{
    return (ll)((ll)(x)*(ll)(x));
}
int main()
{
    read(n);read(m);read(k);
    for (int len,l,r,i=1;i<=n;i++)
        if (read(len),read(l),read(r),l==r)
            addseg(len,0,0),addseg(len,2*m,1);
        else
            if (!l||!r)
                if (!l)addseg(len,0,0),addseg(len,r<0?r+2*m:r,1);
                else addseg(len,l>0?l:2*m+l,0),addseg(len,2*m,1);
            else
                if (l<r)
                {
                    if (l<0&&r>0)addseg(len,0,0),addseg(len,r,1),addseg(len,2*m+l,0),addseg(len,2*m,1);
                    if (l>0&&r>0)addseg(len,l,0),addseg(len,r,1);   
                    if (l<0&&r<0)addseg(len,2*m+l,0),addseg(len,2*m+r,1);
                }           
                else
                {
                    if (l<0&&r<0)addseg(len,2*m+l,0),addseg(len,2*m,1),addseg(len,0,0),addseg(len,2*m+r,1);
                    if (l>0&&r<0)addseg(len,l,0),addseg(len,2*m+r,1);
                    if (l>0&&r>0)addseg(len,l,0),addseg(len,2*m,1),addseg(len,0,0),addseg(len,r,1);
                }
    sort(s+1,s+cnt+1);
    for (int i=1;i<=cnt;i++)
    {
        if (S.size()>=k)ans+=square(S.findrank(k))*(s[i].pos-s[i-1].pos);
        if (!s[i].tag)S.insert(s[i].len);
        else S.Delete(s[i].len);
    }
    if (S.size()>=k)ans+=square(S.findrank(k))*(s[cnt].pos-s[cnt-1].pos);
    printf("%lld\n",ans);
    return 0;
}