jzoj 4996. 【NOI2017模拟3.1】游戏 扫描线+treap

来源:互联网 发布:linux命令怎么退出vi 编辑:程序博客网 时间:2024/05/16 04:39

题意

在无聊的时候,小K和小H会在纸上玩这样一个游戏。
我们可以将纸看做一个平面直角坐标系。小H会先在上面画出n个圆,并把每个圆的圆心以及半径都告诉小K。小H画的n个圆中,任意两个圆不会出现相交或相切的情况。小K需要做的就是从这n个圆中选出若干个圆,使得选出的任意一个圆都不被另一个选出的圆包含。游戏的目标就是要选出尽量多的圆。
游戏一次一次进行着,小K已经对游戏的规则感到了厌倦,所以他决定修改游戏的规则。对于第i个圆,我们定义它的价值为wi。新的游戏目标是使得选出的圆价值和最大(不一定数量最多)。但是圆圈可能很多,或者圆圈的分布非常奇怪,或者小K还有别的事情要做。所以他只好拜托你来帮他求出这个最大值了。
n<=100000

分析

一看就是某种神奇的数据结构题,但是没想到要怎么求包含每个圆的第一个圆。
看了题解才想起了扫描线这种东西2333
就是每个圆在x-r和x+r处拆为两个操作,分别是插入和删除。同时把每个圆分成上半圆和下半圆,然后用一棵treap维护每个半圆。每个节点记录两个值,属于哪个圆和是上半圆还是下半圆,然后根据y值排序。每次插入一个圆后查找其下半圆的后继,若为下半圆则这个圆即为其包含圆;若为上半圆则这两个圆的包含圆相同。
treap打的我生无可恋,看来以后还是要多打treap才行。
记住以后类似于这题这样的平面+数据结构题可以用扫描线做。

代码

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<algorithm>#include<ctime>#include<cmath>#define N 100005#define LL long longusing namespace std;int n,last[N],sz,tot,mx[N],fa[N],now,root,cnt,d[N];struct edge{int to,next;}e[N];struct cyc{int x,y,r,w;}c[N];struct tree{int l,r,fix,id,op;}t[N*2];struct query{int id,val,x;}q[N*2];int read(){    int x=0,f=1;char ch=getchar();    while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}    while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}    return x*f;}bool cmp(query a,query b){    return a.x<b.x;}bool cmpc(cyc a,cyc b){    return a.r<b.r;}int newnode(int id,int op){    t[++sz].id=id;t[sz].op=op;t[sz].fix=rand();    return sz;}LL sqr(int x){    return (LL)x*x;}double get_y(int i,int op){    if (op==0) return (double)1.0*sqrt(sqr(c[i].r)-sqr(c[i].x-now))+(double)c[i].y;    else return (double)c[i].y-(double)1.0*sqrt(sqr(c[i].r)-sqr(c[i].x-now));}void rttr(int &x){    int y=t[x].l;    t[x].l=t[y].r;    t[y].r=x;    x=y;}void rttl(int &x){    int y=t[x].r;    t[x].r=t[y].l;    t[y].l=x;    x=y;}void ins(int &d,int id,int op,double y){    if (!d)    {        d=newnode(id,op);        return;    }    double w=get_y(t[d].id,t[d].op);    if (y>w||y==w&&op<t[d].op)    {        ins(t[d].l,id,op,y);        if (t[t[d].l].fix<t[d].fix) rttr(d);    }    else    {        ins(t[d].r,id,op,y);        if (t[t[d].r].fix>t[d].fix) rttl(d);    }}int findnext(int id,int op,double y){    int x=root,fa=0;    while (x)    {        double w=get_y(t[x].id,t[x].op);        if (y>w||y==w&&op<t[x].op) fa=x,x=t[x].l;        else x=t[x].r;    }    return fa;}void del(int &d,int id,int op,double y){    if (!d) return;    if (t[d].id!=id||t[d].op!=op)    {        double w=get_y(t[d].id,t[d].op);        if (y>w||y==w&&op<t[d].op) del(t[d].l,id,op,y);        else del(t[d].r,id,op,y);    }    else    {        if (!t[d].l||!t[d].r)        {            d=t[d].l+t[d].r;return;        }        if (t[t[d].l].fix<t[t[d].r].fix) rttr(d),del(t[d].r,id,op,y);        else rttl(d),del(t[d].l,id,op,y);    }}void addedge(int u,int v){    e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;}int main(){    //freopen("circle.in","r",stdin);    //freopen("circle.out","w",stdout);    srand((unsigned)time(NULL));    n=read();    for (int i=1;i<=n;i++)    {        c[i].x=read();c[i].y=read();c[i].r=read();c[i].w=read();    }    sort(c+1,c+n+1,cmpc);    for (int i=1;i<=n;i++)    {        q[++tot].x=c[i].x-c[i].r;q[tot].id=i;q[tot].val=1;        q[++tot].x=c[i].x+c[i].r;q[tot].id=i;q[tot].val=-1;    }    sort(q+1,q+tot+1,cmp);    for (int i=1;i<=tot;i++)    {        now=q[i].x;        if (q[i].val==1)        {            ins(root,q[i].id,0,get_y(q[i].id,0)),ins(root,q[i].id,1,get_y(q[i].id,1));            int w=findnext(q[i].id,1,get_y(q[i].id,1));            if (w)            {                if (t[w].op==0) fa[q[i].id]=fa[t[w].id];                else fa[q[i].id]=t[w].id;            }        }        else del(root,q[i].id,0,get_y(q[i].id,0)),del(root,q[i].id,1,get_y(q[i].id,1));    }    for (int i=1;i<=n;i++) if (fa[i]) addedge(fa[i],i),d[i]++;    int ans=0;    for (int i=1;i<=n;i++)    {        for (int j=last[i];j;j=e[j].next) mx[i]+=mx[e[j].to];        mx[i]=max(mx[i],c[i].w);        if (!d[i]) ans+=mx[i];    }    printf("%d",ans);    return 0;}
0 0