[BZOJ]4418: [Shoi2013]扇形面积并 线段树

Description

给定N个同心的扇形,求有多少面积,被至少K个扇形所覆盖。

题解:

对半径开权值线段树,将每个扇形拆成两个操作:加入一条半径或删除一条半径,每进行一次操作,用线段树找当前的第k大(也就是找当前用来算答案的的半径是多少),算答案,即可。

代码:

#include
#include
#include
#include
using namespace std;
#define LL long long
const int maxn=100010;
int n,m,k;
struct node{int x,f,r;}a[maxn*4];
bool cmp(node x,node y){return x.xint len=0;
void work(int r,int a1,int a2)
{
    a[++len].x=a1;a[len].r=r;a[len].f=1;
    a[++len].x=a2;a[len].r=r;a[len].f=-1;
}
struct Tree{int l,r,c,lc,rc;}tr[maxn*2];
int trlen=0;
void build(int l,int r)
{
    int t=++trlen;
    tr[t].l=l;tr[t].r=r;tr[t].c=0;
    if(lint mid=l+r>>1;
        tr[t].lc=trlen+1;build(l,mid);
        tr[t].rc=trlen+1;build(mid+1,r);
    }
}
void add(int now,int p,int x)
{
    if(tr[now].l==tr[now].r){tr[now].c+=x;return;}
    int lc=tr[now].lc,rc=tr[now].rc,mid=tr[now].l+tr[now].r>>1;
    if(p<=mid)add(lc,p,x);
    else add(rc,p,x);
    tr[now].c=tr[lc].c+tr[rc].c;
}
int query(int now,int k)
{
    if(!k)return 0;
    if(tr[now].creturn 0;
    if(tr[now].l==tr[now].r)return tr[now].r;
    int lc=tr[now].lc,rc=tr[now].rc,mid=tr[now].l+tr[now].r>>1;
    if(k<=tr[lc].c)return query(lc,k);
    else return query(rc,k-tr[lc].c);
}
int main()
{
    int maxr=0;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        int r,a1,a2;
        scanf("%d%d%d",&r,&a1,&a2);a1+=m;a2+=m;
        if(a1else work(r,0,a2),work(r,a1,m<<1);
        maxr=max(maxr,r);
    }
    LL ans=0;
    build(1,maxr);
    sort(a+1,a+1+len,cmp);
    for(int i=1;i<=len;i++)
    {
        if(i>1)
        {
            int t=query(1,tr[1].c-k+1);
            if(t&&i>1)ans+=(LL)(t)*(LL)(t)*(LL)(a[i].x-a[i-1].x);
        }
        add(1,a[i].r,a[i].f);
    }
    printf("%lld",ans);
}

你可能感兴趣的:(线段树)